Parcourir la source

[master] Merge branch 'trac2974'

Stephen Morris il y a 12 ans
Parent
commit
d2b107586d

+ 4 - 0
src/lib/util/Makefile.am

@@ -37,6 +37,10 @@ libb10_util_la_SOURCES += encode/base32hex_from_binary.h
 libb10_util_la_SOURCES += encode/base_n.cc encode/hex.h
 libb10_util_la_SOURCES += encode/binary_from_base32hex.h
 libb10_util_la_SOURCES += encode/binary_from_base16.h
+libb10_util_la_SOURCES += hooks/callout_manager.h hooks/callout_manager.cc
+libb10_util_la_SOURCES += hooks/callout_handle.h hooks/callout_handle.cc
+libb10_util_la_SOURCES += hooks/library_handle.h hooks/library_handle.cc
+libb10_util_la_SOURCES += hooks/server_hooks.h hooks/server_hooks.cc
 libb10_util_la_SOURCES += random/qid_gen.h random/qid_gen.cc
 libb10_util_la_SOURCES += random/random_number_generator.h
 

+ 121 - 0
src/lib/util/hooks/callout_handle.cc

@@ -0,0 +1,121 @@
+// 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/callout_manager.h>
+#include <util/hooks/library_handle.h>
+#include <util/hooks/server_hooks.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+using namespace std;
+using namespace isc::util;
+
+namespace isc {
+namespace util {
+
+// Constructor.
+CalloutHandle::CalloutHandle(const boost::shared_ptr<CalloutManager>& manager)
+    : arguments_(), context_collection_(), manager_(manager), skip_(false) {
+
+    // Call the "context_create" hook.  We should be OK doing this - although
+    // the constructor has not finished running, all the member variables
+    // have been created.
+    manager_->callCallouts(ServerHooks::CONTEXT_CREATE, *this);
+}
+
+// Destructor
+CalloutHandle::~CalloutHandle() {
+
+    // Call the "context_destroy" hook.  We should be OK doing this - although
+    // the destructor is being called, all the member variables are still in
+    // existence.
+    manager_->callCallouts(ServerHooks::CONTEXT_DESTROY, *this);
+}
+
+// Return the name of all argument items.
+
+vector<string>
+CalloutHandle::getArgumentNames() const {
+
+    vector<string> names;
+    for (ElementCollection::const_iterator i = arguments_.begin();
+         i != arguments_.end(); ++i) {
+        names.push_back(i->first);
+    }
+
+    return (names);
+}
+
+// Return the library handle allowing the callout to access the CalloutManager
+// registration/deregistration functions.
+
+LibraryHandle&
+CalloutHandle::getLibraryHandle() const {
+    return (manager_->getLibraryHandle());
+}
+
+// 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 = manager_->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 an exception.
+
+const CalloutHandle::ElementCollection&
+CalloutHandle::getContextForLibrary() const {
+    int libindex = manager_->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 index (" << libindex <<
+                  ")");
+    }
+
+    // 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);
+    }
+
+    return (names);
+}
+
+} // namespace util
+} // namespace isc

+ 353 - 0
src/lib/util/hooks/callout_handle.h

@@ -0,0 +1,353 @@
+// 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.
+
+#ifndef CALLOUT_HANDLE_H
+#define CALLOUT_HANDLE_H
+
+#include <exceptions/exceptions.h>
+#include <util/hooks/library_handle.h>
+
+#include <boost/any.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace util {
+
+/// @brief No such argument
+///
+/// Thrown if an attempt is made access an argument that does not exist.
+
+class NoSuchArgument : public Exception {
+public:
+    NoSuchArgument(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+/// @brief No such callout context item
+///
+/// Thrown if an attempt is made to get an item of data from this callout's
+/// context and either the context or an item in the context with that name
+/// does not exist.
+
+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 CalloutManager;
+class LibraryHandle;
+
+/// @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,set}Argument() methods.
+///
+/// - Per-packet context.  Each packet has a context associated with it, this
+///   context being  on a per-library basis.  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, but 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 handle.  Allows the callout to dynamically register and
+///   deregister callouts. (In the latter case, only functions registered by
+///   functions in the same library as the callout doing the deregistration
+///   can be removed: callouts registered by other libraries cannot be
+///   modified.)
+
+class CalloutHandle {
+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 - there is a 1:1 correspondence between libraries
+    /// and a name.value collection.
+    ///
+    /// The collection of contexts is stored in a map, as not every library
+    /// will require creation of 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
+    ///
+    /// Creates the object and calls the callouts on the "context_create"
+    /// hook.
+    ///
+    /// @param manager Pointer to the callout manager object.
+    CalloutHandle(const boost::shared_ptr<CalloutManager>& manager);
+
+    /// @brief Destructor
+    ///
+    /// Calls the context_destroy callback to release any per-packet context.
+    ~CalloutHandle();
+
+    /// @brief Set argument
+    ///
+    /// Sets the value of an argument.  The argument is created if it does not
+    /// already exist.
+    ///
+    /// @param name Name of the argument.
+    /// @param value Value to set.  That can be of any data type.
+    template <typename T>
+    void setArgument(const std::string& name, T value) {
+        arguments_[name] = value;
+    }
+
+    /// @brief Get argument
+    ///
+    /// Gets the value of an argument.
+    ///
+    /// @param name Name of the element in the argument list to get.
+    /// @param value [out] Value to set.  The type of "value" is important:
+    ///        it must match the type of the value set.
+    ///
+    /// @throw NoSuchArgument No argument with the given name is present.
+    /// @throw boost::bad_any_cast An argument with the given name is present,
+    ///        but the data type of the value is not the same as the type of
+    ///        the variable provided to receive the value.
+    template <typename T>
+    void getArgument(const std::string& name, T& value) const {
+        ElementCollection::const_iterator element_ptr = arguments_.find(name);
+        if (element_ptr == arguments_.end()) {
+            isc_throw(NoSuchArgument, "unable to find argument with name " <<
+                      name);
+        }
+
+        value = boost::any_cast<T>(element_ptr->second);
+    }
+    
+    /// @brief Get argument names
+    ///
+    /// Returns a vector holding the names of arguments in the argument
+    /// vector.
+    ///
+    /// @return Vector of strings reflecting argument names.
+    std::vector<std::string> getArgumentNames() const;
+
+    /// @brief Delete argument
+    ///
+    /// 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 method.
+    ///
+    /// @param name Name of the element in the argument list to set.
+    void deleteArgument(const std::string& name) {
+        static_cast<void>(arguments_.erase(name));
+    }
+
+    /// @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();
+    }
+
+    /// @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 associated
+    /// with the current hook should be bypassed.
+    ///
+    /// @param skip New value of the "skip" flag.
+    void setSkip(bool skip) {
+        skip_ = skip;
+    }
+
+    /// @brief Get skip flag
+    ///
+    /// Gets the current value of the "skip" flag.
+    ///
+    /// @return Current value of the skip flag.
+    bool getSkip() const {
+        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 uses the library handle to
+    /// dynamically register or deregister callouts.
+    ///
+    /// @return Reference to the library handle.
+    ///
+    /// @throw InvalidIndex thrown if this method is called when the current
+    ///        library index is invalid (typically if it is called outside of
+    ///        the active callout).
+    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 data is not the same as the type of the
+    ///        variable provided to receive its value.
+    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");
+        }
+
+        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 the names of items in the callout
+    ///         context associated with the current library.
+    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 context item to delete.
+    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.
+    ///
+    /// @throw InvalidIndex current library index is not valid for the library
+    ///        handle collection.
+    int getLibraryIndex() const;
+
+    /// @brief Return reference to context for current library
+    ///
+    /// Called by all context-setting functions, this returns a reference to
+    /// the callout context for the current library, creating a context if it
+    /// does not exist.
+    ///
+    /// @return Reference to the collection of name/value pairs associated
+    ///         with the current library.
+    ///
+    /// @throw InvalidIndex current library index is not valid for the library
+    ///        handle collection.
+    ElementCollection& getContextForLibrary();
+
+    /// @brief Return reference to context for current library (const version)
+    ///
+    /// Called by all context-accessing functions, this a reference to the
+    /// callout context for the current library.  An exception is thrown if
+    /// it does not exist.
+    ///
+    /// @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
+    ElementCollection arguments_;
+
+    /// Context collection - there is one entry per library context.
+    ContextCollection context_collection_;
+
+    /// Callout manager.
+    boost::shared_ptr<CalloutManager> manager_;
+
+    /// "Skip" flag, indicating if the caller should bypass remaining callouts.
+    bool skip_;
+};
+
+} // namespace util
+} // namespace isc
+
+
+#endif // CALLOUT_HANDLE_H

+ 182 - 0
src/lib/util/hooks/callout_manager.cc

@@ -0,0 +1,182 @@
+// 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/callout_manager.h>
+
+#include <boost/static_assert.hpp>
+
+#include <algorithm>
+#include <functional>
+#include <utility>
+
+using namespace std;
+using namespace isc::util;
+
+namespace isc {
+namespace util {
+
+// Register a callout for the current library.
+
+void
+CalloutManager::registerCallout(const std::string& name, CalloutPtr callout) {
+    // Sanity check that the current library index is set to a valid value.
+    checkLibraryIndex(current_library_);
+
+    // Get the index associated with this hook (validating the name in the
+    // process).
+    int hook_index = hooks_->getIndex(name);
+
+    // Iterate through the callout vector for the hook from start to end,
+    // looking for the first entry where the library index is greater than
+    // the present index.
+    for (CalloutVector::iterator i = hook_vector_[hook_index].begin();
+         i != hook_vector_[hook_index].end(); ++i) {
+        if (i->first > current_library_) {
+            // Found an element whose library index number is greater than the
+            // current index, so insert the new element ahead of this one.
+            hook_vector_[hook_index].insert(i, make_pair(current_library_,
+                                                         callout));
+            return;
+        }
+    }
+
+    // Reached the end of the vector, so there is no element in the (possibly
+    // empty) set of callouts with a library index greater than the current
+    // library index.  Inset the callout at the end of the list.
+    hook_vector_[hook_index].push_back(make_pair(current_library_, callout));
+}
+
+// Check if callouts are present for a given hook index.
+
+bool
+CalloutManager::calloutsPresent(int hook_index) const {
+    // Validate the hook index.
+    if ((hook_index < 0) || (hook_index >= hook_vector_.size())) {
+        isc_throw(NoSuchHook, "hook index " << hook_index <<
+                  " is not valid for the list of registered hooks");
+    }
+
+    // Valid, so are there any callouts associated with that hook?
+    return (!hook_vector_[hook_index].empty());
+}
+
+// Call all the callouts for a given hook.
+
+void
+CalloutManager::callCallouts(int hook_index, CalloutHandle& callout_handle) {
+
+    // Only initialize and iterate if there are callouts present.  This check
+    // also catches the case of an invalid index.
+    if (calloutsPresent(hook_index)) {
+
+        // Clear the "skip" flag so we don't carry state from a previous call.
+        callout_handle.setSkip(false);
+
+        // Duplicate the callout vector for this hook and work through that.
+        // This step is needed because we allow dynamic registration and
+        // deregistration of callouts.  If a callout attached to a hook modified
+        // the list of callouts on that hook, the underlying CalloutVector would
+        // change and potentially affect the iteration through that vector.
+        CalloutVector callouts(hook_vector_[hook_index]);
+
+        // Call all the callouts.
+        for (CalloutVector::const_iterator i = callouts.begin();
+             i != callouts.end(); ++i) {
+            // In case the callout tries to register or deregister a callout,
+            // set the current library index to the index associated with the
+            // library that registered the callout being called.
+            current_library_ = i->first;
+
+            // Call the callout
+            // @todo Log the return status if non-zero
+            static_cast<void>((*i->second)(callout_handle));
+        }
+
+        // Reset the current library index to an invalid value to catch any
+        // programming errors.
+        current_library_ = -1;
+    }
+}
+
+// Deregister a callout registered by the current library on a particular hook.
+
+bool
+CalloutManager::deregisterCallout(const std::string& name, CalloutPtr callout) {
+    // Sanity check that the current library index is set to a valid value.
+    checkLibraryIndex(current_library_);
+
+    // Get the index associated with this hook (validating the name in the
+    // process).
+    int hook_index = hooks_->getIndex(name);
+
+    /// Construct a CalloutEntry matching the current library and the callout
+    /// we want to remove.
+    CalloutEntry target(current_library_, callout);
+
+    /// To decide if any entries were removed, we'll record the initial size
+    /// of the callout vector for the hook, and compare it with the size after
+    /// the removal.
+    size_t initial_size = hook_vector_[hook_index].size();
+
+    // The next bit is standard STL (see "Item 33" in "Effective STL" by
+    // Scott Meyers).
+    //
+    // remove_if reorders the hook vector so that all items not matching
+    // the predicate are at the start of the vector and returns a pointer
+    // to the next element. (In this case, the predicate is that the item
+    // is equal to the value of the passed callout.)  The erase() call
+    // removes everything from that element to the end of the vector, i.e.
+    // all the matching elements.
+    hook_vector_[hook_index].erase(remove_if(hook_vector_[hook_index].begin(),
+                                             hook_vector_[hook_index].end(),
+                                             bind1st(equal_to<CalloutEntry>(),
+                                                     target)),
+                                   hook_vector_[hook_index].end());
+
+    // Return an indication of whether anything was removed.
+    return (initial_size != hook_vector_[hook_index].size());
+}
+
+// Deregister all callouts on a given hook.
+
+bool
+CalloutManager::deregisterAllCallouts(const std::string& name) {
+
+    // Get the index associated with this hook (validating the name in the
+    // process).
+    int hook_index = hooks_->getIndex(name);
+
+    /// Construct a CalloutEntry matching the current library (the callout
+    /// pointer is NULL as we are not checking that).
+    CalloutEntry target(current_library_, NULL);
+
+    /// To decide if any entries were removed, we'll record the initial size
+    /// of the callout vector for the hook, and compare it with the size after
+    /// the removal.
+    size_t initial_size = hook_vector_[hook_index].size();
+
+    // Remove all callouts matching this library.
+    hook_vector_[hook_index].erase(remove_if(hook_vector_[hook_index].begin(),
+                                             hook_vector_[hook_index].end(),
+                                             bind1st(CalloutLibraryEqual(),
+                                                     target)),
+                                   hook_vector_[hook_index].end());
+
+    // Return an indication of whether anything was removed.
+    return (initial_size != hook_vector_[hook_index].size());
+}
+
+} // namespace util
+} // namespace isc

+ 301 - 0
src/lib/util/hooks/callout_manager.h

@@ -0,0 +1,301 @@
+// 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.
+
+#ifndef CALLOUT_MANAGER_H
+#define CALLOUT_MANAGER_H
+
+#include <exceptions/exceptions.h>
+#include <util/hooks/library_handle.h>
+#include <util/hooks/server_hooks.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <map>
+#include <string>
+
+namespace isc {
+namespace util {
+
+/// @brief No such library
+///
+/// Thrown if an attempt is made to set the current library index to a value
+/// that is invalid for the number of loaded libraries.
+class NoSuchLibrary : public Exception {
+public:
+    NoSuchLibrary(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+
+/// @brief Callout Manager
+///
+/// This class manages the registration, deregistration and execution of the
+/// library callouts.
+///
+/// In operation, the class needs to know two items of data:
+///
+/// - The list of server hooks, which is used in two ways.  Firstly, when a
+///   callout registers or deregisters a hook, it does so by name: the
+///   @ref isc::util::ServerHooks object supplies the names of registered
+///   hooks.  Secondly, when the callouts associated with a hook are called by
+///   the server, the server supplies the index of the relevant hook: this is
+///   validated by reference to the list of hook.
+///
+/// - The number of loaded libraries.  Each callout registered by a user
+///   library is associated with that library, the callout manager storing both
+///   a pointer to the callout and the index of the library in the list of
+///   loaded libraries.  Callouts are allowed to dynamically register and
+///   deregister callouts in the same library (including themselves): they
+///   cannot affect callouts registered by another library.  When calling a
+///   callout, the callout manager maintains the idea of a "current library
+///   index": if the callout calls one of the callout registration functions 
+///   (indirectly via the @ref LibraryHandle object), the registration
+///   functions use the "current library index" in their processing.
+///
+/// These two items of data are supplied when an object of this class is
+/// constructed.
+///
+/// Note that the callout function do not access the library manager: instead,
+/// they use a LibraryHandle object.  This contains an internal pointer to
+/// the CalloutManager, but provides a restricted interface.  In that way,
+/// callouts are unable to affect callouts supplied by other libraries.
+
+class CalloutManager {
+private:
+
+    // Private typedefs
+
+    /// Element in the vector of callouts.  The elements in the pair are the
+    /// index of the library from which this callout was registered, and a#
+    /// pointer to the callout itself.
+    typedef std::pair<int, CalloutPtr> CalloutEntry;
+
+    /// An element in the hook vector. Each element is a vector of callouts
+    /// associated with a given hook.
+    typedef std::vector<CalloutEntry> CalloutVector;
+
+public:
+
+    /// @brief Constructor
+    ///
+    /// Initializes member variables, in particular sizing the hook vector
+    /// (the vector of callout vectors) to the appropriate size.
+    ///
+    /// @param hooks Collection of known hook names.
+    /// @param num_libraries Number of loaded libraries.
+    ///
+    /// @throw isc::BadValue if the number of libraries is less than or equal
+    ///        to 0, or if the pointer to the server hooks object is empty.
+    CalloutManager(const boost::shared_ptr<ServerHooks>& hooks,
+                   int num_libraries)
+        : current_library_(-1), hooks_(hooks), hook_vector_(),
+          library_handle_(this), num_libraries_(num_libraries)
+    {
+        if (!hooks) {
+            isc_throw(isc::BadValue, "must pass a pointer to a valid server "
+                      "hooks object to the CalloutManager");
+        } else if (num_libraries <= 0) {
+            isc_throw(isc::BadValue, "number of libraries passed to the "
+                      "CalloutManager must be >= 0");
+        }
+
+        // Parameters OK, do operations that depend on them.
+        hook_vector_.resize(hooks_->getCount());
+    }
+
+    /// @brief Register a callout on a hook for the current library
+    ///
+    /// Registers a callout function for the current library with a given hook
+    /// (the index of the "current library" being given by the current_library_
+    /// member).  The callout is added to the end of the callouts for this
+    /// library that are associated with that hook.
+    ///
+    /// @param name Name of the hook to which the callout is added.
+    /// @param callout Pointer to the callout function to be registered.
+    ///
+    /// @throw NoSuchHook The hook name is unrecognised.
+    /// @throw Unexpected The hook name is valid but an internal data structure
+    ///        is of the wrong size.
+    void registerCallout(const std::string& name, CalloutPtr callout);
+
+    /// @brief De-Register a callout on a hook for the current library
+    ///
+    /// Searches through the functions registered by the the current library
+    /// (the index of the "current library" being given by the current_library_
+    /// member) with the named hook and removes all entries matching the
+    /// callout.
+    ///
+    /// @param name Name of the hook from which the callout is removed.
+    /// @param callout Pointer to the callout function to be removed.
+    ///
+    /// @return true if a one or more callouts were deregistered.
+    ///
+    /// @throw NoSuchHook The hook name is unrecognised.
+    /// @throw Unexpected The hook name is valid but an internal data structure
+    ///        is of the wrong size.
+    bool deregisterCallout(const std::string& name, CalloutPtr callout);
+
+    /// @brief Removes all callouts on a hook for the current library
+    ///
+    /// Removes all callouts associated with a given hook that were registered
+    /// by the current library (the index of the "current library" being given
+    /// by the current_library_ member).
+    ///
+    /// @param name Name of the hook from which the callouts are removed.
+    ///
+    /// @return true if one or more callouts were deregistered.
+    ///
+    /// @throw NoSuchHook Thrown if the hook name is unrecognised.
+    bool deregisterAllCallouts(const std::string& name);
+
+    /// @brief Checks if callouts are present on a hook
+    ///
+    /// Checks all loaded libraries and returns true if at least one callout
+    /// has been registered by any of them for the given hook.
+    ///
+    /// @param hook_index Hook index for which callouts are checked.
+    ///
+    /// @return true if callouts are present, false if not.
+    ///
+    /// @throw NoSuchHook Given index does not correspond to a valid hook.
+    bool calloutsPresent(int hook_index) const;
+
+    /// @brief Calls the callouts for a given hook
+    ///
+    /// Iterates through the libray handles and calls the callouts associated
+    /// with the given hook index.
+    ///
+    /// @note This method invalidates the current library index set with
+    ///       setLibraryIndex().
+    ///
+    /// @param hook_index Index of the hook to call.
+    /// @param callout_handle Reference to the CalloutHandle object for the
+    ///        current object being processed.
+    void callCallouts(int hook_index, CalloutHandle& callout_handle);
+
+    /// @brief Get number of libraries
+    ///
+    /// Returns the number of libraries that this CalloutManager is expected
+    /// to serve.  This is the number passed to its constructor.
+    ///
+    /// @return Number of libraries server by this CalloutManager.
+    int getNumLibraries() const {
+        return (num_libraries_);
+    }
+
+    /// @brief Get current library index
+    ///
+    /// Returns the index of the "current" library.  This the index associated
+    /// with the currently executing callout when callCallouts is executing.
+    /// When callCallouts() is not executing (as is the case when the load()
+    /// function in a user-library is called during the library load process),
+    /// the index can be set by setLibraryIndex().
+    ///
+    /// @note The value set by this method is lost after a call to
+    ///       callCallouts.
+    ///
+    /// @return Current library index.
+    int getLibraryIndex() const {
+        return (current_library_);
+    }
+
+    /// @brief Set current library index
+    ///
+    /// Sets the current library index.  This must be in the range 0 to
+    /// (numlib - 1), where "numlib" is the number of libraries loaded in the
+    /// system, this figure being passed to this object at construction time.
+    ///
+    /// @param library_index New library index.
+    ///
+    /// @throw NoSuchLibrary if the index is not valid.
+    void setLibraryIndex(int library_index) {
+        checkLibraryIndex(library_index);
+        current_library_ = library_index;
+    }
+
+    /// @brief Return library handle
+    ///
+    /// The library handle is available to the user callout via the callout
+    /// handle object.  It provides a cut-down view of the CalloutManager,
+    /// allowing the callout to register and deregister callouts in the
+    /// library of which it is part, whilst denying access to anything that
+    /// may affect other libraries.
+    ///
+    /// @return Reference to callout handle for this manager
+    LibraryHandle& getLibraryHandle() {
+        return (library_handle_);
+    }
+
+private:
+    /// @brief Check library index
+    ///
+    /// Ensures that the current library index is valid.  This is called by
+    /// the hook registration functions.
+    ///
+    /// @param library_index Value to check for validity as a library index.
+    ///
+    /// @throw NoSuchLibrary Library index is not valid.
+    void checkLibraryIndex(int library_index) const {
+        if ((library_index < 0) || (library_index >= num_libraries_)) {
+            isc_throw(NoSuchLibrary, "library index " << library_index <<
+                      " is not valid for the number of loaded libraries (" <<
+                      num_libraries_ << ")");
+        }
+    }
+
+    /// @brief Compare two callout entries for library equality
+    ///
+    /// This is used in callout removal code when all callouts on a hook for a
+    /// given library are being removed.  It checks whether two callout entries
+    /// have the same library index.
+    ///
+    /// @param ent1 First callout entry to check
+    /// @param ent2 Second callout entry to check
+    ///
+    /// @return bool true if the library entries are the same
+    class CalloutLibraryEqual :
+        public std::binary_function<CalloutEntry, CalloutEntry, bool> {
+    public:
+        bool operator()(const CalloutEntry& ent1,
+                        const CalloutEntry& ent2) const {
+            return (ent1.first == ent2.first);
+        }
+    };
+
+    /// Current library index.  When a call is made to any of the callout
+    /// registration methods, this variable indicates the index of the user
+    /// library that should be associated with the call.
+    int current_library_;
+
+    /// List of server hooks.
+    boost::shared_ptr<ServerHooks>  hooks_;
+
+    /// Vector of callout vectors.  There is one entry in this outer vector for
+    /// each hook. Each element is itself a vector, with one entry for each
+    /// callout registered for that hook.
+    std::vector<CalloutVector>  hook_vector_;
+
+    /// LibraryHandle object user by the callout to access the callout
+    /// registration methods on this CalloutManager object.
+    LibraryHandle library_handle_;
+
+    /// Number of libraries.
+    int num_libraries_;
+
+};
+
+} // namespace util
+} // namespace isc
+
+#endif // CALLOUT_MANAGER_H

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

@@ -0,0 +1,39 @@
+// 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_manager.h>
+#include <util/hooks/library_handle.h>
+
+namespace isc {
+namespace util {
+
+// Callout manipulation - all deferred to the CalloutManager.
+
+void
+LibraryHandle::registerCallout(const std::string& name, CalloutPtr callout) {
+    callout_manager_->registerCallout(name, callout);
+}
+
+bool
+LibraryHandle::deregisterCallout(const std::string& name, CalloutPtr callout) {
+    return (callout_manager_->deregisterCallout(name, callout));
+}
+
+bool
+LibraryHandle::deregisterAllCallouts(const std::string& name) {
+    return (callout_manager_->deregisterAllCallouts(name));
+}
+
+} // namespace util
+} // namespace isc

+ 115 - 0
src/lib/util/hooks/library_handle.h

@@ -0,0 +1,115 @@
+// 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.
+
+#ifndef LIBRARY_HANDLE_H
+#define LIBRARY_HANDLE_H
+
+#include <string>
+
+namespace isc {
+namespace util {
+
+// Forward declarations
+class CalloutHandle;
+class CalloutManager;
+
+/// Typedef for a callout pointer.  (Callouts must have "C" linkage.)
+extern "C" {
+    typedef int (*CalloutPtr)(CalloutHandle&);
+};
+
+/// @brief Library handle
+///
+/// This class is accessed by the user library when registering callouts,
+/// either by the library's load() function, or by one of the callouts
+/// themselves.
+///
+/// It is really little more than a shell around the CalloutManager.  By
+/// presenting this object to the user-library callouts, callouts can manage
+/// the callout list for their own library, but cannot affect the callouts
+/// registered by other libraries.
+///
+/// (This restriction is achieved by the CalloutManager maintaining the concept
+/// of the "current library".  When a callout is registered - either by the
+/// library's load() function, or by a callout in the library - the registration
+/// information includes the library active at the time.  When that callout is
+/// called, the CalloutManager uses that information to set the "current
+/// library": the registration functions only operator on data whose
+/// associated library is equal to the "current library".)
+
+class LibraryHandle {
+public:
+
+    /// @brief Constructor
+    ///
+    /// @param manager Back pointer to the containing CalloutManager.
+    ///        This pointer is used to access appropriate methods in that
+    ///        object.  Note that the "raw" pointer is safe - the only
+    ///        instance of the LibraryHandle in the system is as a member of
+    ///        the CalloutManager to which it points.
+    LibraryHandle(CalloutManager* manager) : callout_manager_(manager)
+    {}
+
+    /// @brief Register a callout on a hook
+    ///
+    /// Registers a callout function with a given hook.  The callout is added
+    /// to the end of the callouts for the current library that are associated
+    /// with that hook.
+    ///
+    /// @param name Name of the hook to which the callout is added.
+    /// @param callout Pointer to the callout function to be registered.
+    ///
+    /// @throw NoSuchHook The hook name is unrecognised.
+    /// @throw Unexpected The hook name is valid but an internal data structure
+    ///        is of the wrong size.
+    void registerCallout(const std::string& name, CalloutPtr callout);
+
+    /// @brief De-Register a callout on a hook
+    ///
+    /// Searches through the functions registered by the current library with
+    /// the named hook and removes all entries matching the callout.  It does
+    /// not affect callouts registered by other libraries.
+    ///
+    /// @param name Name of the hook from which the callout is removed.
+    /// @param callout Pointer to the callout function to be removed.
+    ///
+    /// @return true if a one or more callouts were deregistered.
+    ///
+    /// @throw NoSuchHook The hook name is unrecognised.
+    /// @throw Unexpected The hook name is valid but an internal data structure
+    ///        is of the wrong size.
+    bool deregisterCallout(const std::string& name, CalloutPtr callout);
+
+    /// @brief Removes all callouts on a hook
+    ///
+    /// Removes all callouts associated with a given hook that were registered.
+    /// by the current library.  It does not affect callouts that were
+    /// registered by other libraries.
+    ///
+    /// @param name Name of the hook from which the callouts are removed.
+    ///
+    /// @return true if one or more callouts were deregistered.
+    ///
+    /// @throw NoSuchHook Thrown if the hook name is unrecognised.
+    bool deregisterAllCallouts(const std::string& name);
+
+private:
+    /// Back pointer to the collection object for the library
+    CalloutManager* callout_manager_;
+};
+
+} // namespace util
+} // namespace isc
+
+#endif // LIBRARY_HANDLE_H

+ 125 - 0
src/lib/util/hooks/server_hooks.cc

@@ -0,0 +1,125 @@
+// 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 <exceptions/exceptions.h>
+#include <util/hooks/server_hooks.h>
+
+#include <utility>
+#include <vector>
+
+using namespace std;
+using namespace isc;
+
+namespace isc {
+namespace util {
+
+// Constructor - register the pre-defined hooks and check that the indexes
+// assigned to them are as expected.
+
+ServerHooks::ServerHooks() {
+    int create = registerHook("context_create");
+    int destroy = registerHook("context_destroy");
+
+    if ((create != CONTEXT_CREATE) || (destroy != CONTEXT_DESTROY)) {
+        isc_throw(Unexpected, "pre-defined hook indexes are not as expected. "
+                  "context_create: expected = " << CONTEXT_CREATE <<
+                  ", actual = " << create <<
+                  ". context_destroy: expected = " << CONTEXT_DESTROY <<
+                  ", actual = " << destroy);
+    }
+}
+
+// Register a hook.  The index assigned to the hook is the current number
+// of entries in the collection, so ensuring that hook indexes are unique
+// and non-negative.
+
+int
+ServerHooks::registerHook(const string& name) {
+
+    // Determine index for the new element and insert.
+    int index = hooks_.size();
+    pair<HookCollection::iterator, bool> result =
+        hooks_.insert(make_pair(name, index));
+
+    if (!result.second) {
+        // New element was not inserted because an element with the same name
+        // already existed.
+        isc_throw(DuplicateHook, "hook with name " << name <<
+                  " is already registered");
+    }
+
+    // New element inserted, return numeric index.
+    return (index);
+}
+
+// Find the index associated with a hook name.
+
+int
+ServerHooks::getIndex(const string& name) const {
+
+    // Get iterator to matching element.
+    HookCollection::const_iterator i = hooks_.find(name);
+    if (i == hooks_.end()) {
+        isc_throw(NoSuchHook, "hook name " << name << " is not recognised");
+    }
+
+    return (i->second);
+}
+
+// Return vector of hook names.  The names are not sorted - it is up to the
+// caller to perform sorting if required.
+
+vector<string>
+ServerHooks::getHookNames() const {
+
+    vector<string> names;
+    HookCollection::const_iterator i;
+    for (i = hooks_.begin(); i != hooks_.end(); ++i) {
+        names.push_back(i->first);
+    }
+
+    return (names);
+}
+
+// Hook registration function methods
+
+// Access the hook registration function vector itself
+
+std::vector<HookRegistrationFunction::RegistrationFunctionPtr>&
+HookRegistrationFunction::getFunctionVector() {
+    static std::vector<RegistrationFunctionPtr> reg_functions;
+    return (reg_functions);
+}
+
+// Constructor - add a registration function to the function vector
+
+HookRegistrationFunction::HookRegistrationFunction(
+    HookRegistrationFunction::RegistrationFunctionPtr reg_func) {
+    getFunctionVector().push_back(reg_func);
+}
+
+// Execute all registered registration functions
+
+void
+HookRegistrationFunction::execute(ServerHooks& hooks) {
+    std::vector<RegistrationFunctionPtr>& reg_functions = getFunctionVector();
+    for (int i = 0; i < reg_functions.size(); ++i) {
+        (*reg_functions[i])(hooks);
+    }
+}
+
+
+
+} // namespace util
+} // namespace isc

+ 207 - 0
src/lib/util/hooks/server_hooks.h

@@ -0,0 +1,207 @@
+// 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.
+
+#ifndef SERVER_HOOKS_H
+#define SERVER_HOOKS_H
+
+#include <exceptions/exceptions.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace util {
+
+/// @brief Duplicate hook
+///
+/// Thrown if an attempt is made to register a hook with the same name as a
+/// previously-registered hook.
+class DuplicateHook : public Exception {
+public:
+    DuplicateHook(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+/// @brief Invalid hook
+///
+/// Thrown if an attempt is made to get the index for an invalid hook.
+class NoSuchHook : public Exception {
+public:
+    NoSuchHook(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+
+/// @brief Server hook collection
+///
+/// This class is used by the server-side code to register hooks - points in the
+/// server processing at which libraries can register functions (callouts) that
+/// the server will call.  These functions can modify data and so affect the
+/// processing of the server.
+///
+/// The ServerHooks class is little more than a wrapper around the std::map
+/// class.  It stores a hook, assigning to it a unique index number.  This
+/// number is then used by the server code to identify the hook being called.
+/// (Although it would be feasible to use a name as an index, using an integer
+/// will speed up the time taken to locate the callouts, which may make a
+/// difference in a frequently-executed piece of code.)
+
+class ServerHooks {
+public:
+
+    /// Index numbers for pre-defined hooks.
+    static const int CONTEXT_CREATE = 0;
+    static const int CONTEXT_DESTROY = 1;
+
+    /// @brief Constructor
+    ///
+    /// This pre-registers two hooks, context_create and context_destroy, which
+    /// are called by the server before processing a packet and after processing
+    /// for the packet has completed.  They allow the server code to allocate
+    /// and destroy per-packet context.
+    ///
+    /// @throws isc::Unexpected if the registration of the pre-defined hooks
+    ///         fails in some way.
+    ServerHooks();
+
+    /// @brief Register a hook
+    ///
+    /// Registers a hook and returns the hook index.
+    ///
+    /// @param name Name of the hook
+    ///
+    /// @return Index of the hook, to be used in subsequent hook-related calls.
+    ///         This will be greater than or equal to zero (so allowing a
+    ///         negative value to indicate an invalid index).
+    ///
+    /// @throws DuplicateHook A hook with the same name has already been
+    ///         registered.
+    int registerHook(const std::string& name);
+
+    /// @brief Get hook index
+    ///
+    /// Returns the index of a hook.
+    ///
+    /// @param name Name of the hook
+    ///
+    /// @return Index of the hook, to be used in subsequent calls.
+    ///
+    /// @throw NoSuchHook if the hook name is unknown to the caller.
+    int getIndex(const std::string& name) const;
+
+    /// @brief Return number of hooks
+    ///
+    /// Returns the total number of hooks registered.
+    ///
+    /// @return Number of hooks registered.
+    int getCount() const {
+        return (hooks_.size());
+    }
+
+    /// @brief Get hook names
+    ///
+    /// Return list of hooks registered in the object.
+    ///
+    /// @return Vector of strings holding hook names.
+    std::vector<std::string> getHookNames() const;
+
+private:
+    typedef std::map<std::string, int> HookCollection;
+
+    HookCollection  hooks_;     ///< Hook name/index collection
+};
+
+
+/// @brief Hooks Registration
+///
+/// All hooks must be registered before libraries are loaded and callouts
+/// assigned to them.  One way of doing this is to have a global list of hooks:
+/// the addition of any hook anywhere would require updating the list. This
+/// is possible and, if desired, the author of a server can do it.
+///
+/// An alternative is the option provided here, where each component of BIND 10
+/// registers the hooks they are using.  To do this, the component should
+/// create a hook registration function of the form:
+///
+/// @code
+/// static int hook1_num = -1;  // Initialize number for hook 1
+/// static int hook2_num = -1;  // Initialize number for hook 2
+///
+/// void myModuleRegisterHooks(ServerHooks& hooks) {
+///     hook1_num = hooks.registerHook("hook1");
+///     hook2_num = hooks.registerHook("hook2");
+/// }
+/// @endcode
+///
+/// ... which registers the hooks and stores the associated hook index. To
+/// avoid the need to add an explicit call to each of the hook registration
+/// functions to the server initialization code, the component should declare
+/// an object of this class in the same file as the registration function,
+/// but outside of any function.  The declaration should include the name
+/// of the registration function, i.e.
+///
+/// @code
+/// HookRegistrationFunction f(myModuleRegisterHooks);
+/// @code
+///
+/// The constructor of this object will run prior to main() getting called and
+/// will add the registration function to a list of such functions.  The server
+/// then calls the static class method "execute()" to run all the declared
+/// registration functions.
+
+class HookRegistrationFunction {
+public:
+    /// @brief Pointer to a hook registration function
+    typedef void (*RegistrationFunctionPtr)(ServerHooks&);
+
+    /// @brief Constructor
+    ///
+    /// For variables declared outside functions or methods, the constructors
+    /// are run after the program is loaded and before main() is called. This
+    /// constructor adds the passed pointer to a vector of such pointers.
+    HookRegistrationFunction(RegistrationFunctionPtr reg_func);
+
+    /// @brief Access registration function vector
+    ///
+    /// One of the problems with functions run prior to starting main() is the
+    /// "static initialization fiasco".  This occurs because the order in which
+    /// objects outside functions are constructed is not defined.  So if this
+    /// constructor were to depend on a vector declared externally, we would
+    /// not be able to guarantee that the vector had been initialised properly
+    /// before we used it.
+    ///
+    /// To get round this situation, the vector is declared statically within
+    /// a static function.  The first time the function is called, the vector
+    /// is initialized before it is used.
+    ///
+    /// This function returns a reference to the vector used to hold the
+    /// pointers.
+    ///
+    /// @return Reference to the (static) list of registration functions
+    static std::vector<RegistrationFunctionPtr>& getFunctionVector();
+
+    /// @brief Execute registration functions
+    ///
+    /// Called by the server initialization code, this function executes all
+    /// registered hook registration functions.
+    ///
+    /// @param hooks ServerHooks object to which hook information will be added.
+    static void execute(ServerHooks& hooks);
+};
+
+} // namespace util
+} // namespace isc
+
+#endif  // SERVER_HOOKS_H

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

@@ -25,10 +25,13 @@ run_unittests_SOURCES  = run_unittests.cc
 run_unittests_SOURCES += base32hex_unittest.cc
 run_unittests_SOURCES += base64_unittest.cc
 run_unittests_SOURCES += buffer_unittest.cc
+run_unittests_SOURCES += callout_handle_unittest.cc
+run_unittests_SOURCES += callout_manager_unittest.cc
 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 += lru_list_unittest.cc
 run_unittests_SOURCES += memory_segment_local_unittest.cc
@@ -39,6 +42,7 @@ run_unittests_SOURCES += memory_segment_common_unittest.h
 run_unittests_SOURCES += memory_segment_common_unittest.cc
 run_unittests_SOURCES += qid_gen_unittest.cc
 run_unittests_SOURCES += random_number_generator_unittest.cc
+run_unittests_SOURCES += server_hooks_unittest.cc
 run_unittests_SOURCES += sha1_unittest.cc
 run_unittests_SOURCES += socketsession_unittest.cc
 run_unittests_SOURCES += strutil_unittest.cc

+ 329 - 0
src/lib/util/tests/callout_handle_unittest.cc

@@ -0,0 +1,329 @@
+// 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/callout_manager.h>
+#include <util/hooks/library_handle.h>
+#include <util/hooks/server_hooks.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <gtest/gtest.h>
+
+using namespace isc::util;
+using namespace std;
+
+namespace {
+
+/// @file
+/// @brief Holds the CalloutHandle argument tests
+///
+/// Additional testing of the CalloutHandle - together with the interaction
+/// of the LibraryHandle - is done in the handles_unittests set of tests.
+
+class CalloutHandleTest : public ::testing::Test {
+public:
+
+    /// @brief Constructor
+    ///
+    /// Sets up a callout manager to be referenced by the CalloutHandle in
+    /// these tests. (The "4" for the number of libraries in the
+    /// CalloutManager is arbitrary - it is not used in these tests.)
+    CalloutHandleTest()
+        : hooks_(new ServerHooks()), manager_(new CalloutManager(hooks_, 4))
+    {}
+
+    /// Obtain hook manager
+    boost::shared_ptr<CalloutManager>& getCalloutManager() {
+        return (manager_);
+    }
+
+private:
+    /// List of server hooks
+    boost::shared_ptr<ServerHooks> hooks_;
+
+    /// Callout manager accessed by this CalloutHandle.
+    boost::shared_ptr<CalloutManager> manager_;
+};
+
+// *** Argument Tests ***
+//
+// The first set of tests check that the CalloutHandle can store and retrieve
+// arguments.  These are very similar to the LibraryHandle context tests.
+
+// Test that we can store multiple values of the same type and that they
+// are distinct.
+
+TEST_F(CalloutHandleTest, ArgumentDistinctSimpleType) {
+    CalloutHandle handle(getCalloutManager());
+
+    // Store and retrieve an int (random value).
+    int a = 42;
+    handle.setArgument("integer1", a);
+    EXPECT_EQ(42, a);
+
+    int b = 0;
+    handle.getArgument("integer1", b);
+    EXPECT_EQ(42, b);
+
+    // Add another integer (another random value).
+    int c = 142;
+    handle.setArgument("integer2", c);
+    EXPECT_EQ(142, c);
+
+    int d = 0;
+    handle.getArgument("integer2", d);
+    EXPECT_EQ(142, d);
+
+    // Add a short (random value).
+    short e = -81; 
+    handle.setArgument("short", e);
+    EXPECT_EQ(-81, e);
+
+    short f = 0;
+    handle.getArgument("short", f);
+    EXPECT_EQ(-81, f);
+}
+
+// Test that trying to get an unknown argument throws an exception.
+
+TEST_F(CalloutHandleTest, ArgumentUnknownName) {
+    CalloutHandle handle(getCalloutManager());
+
+    // Set an integer
+    int a = 42;
+    handle.setArgument("integer1", a);
+    EXPECT_EQ(42, a);
+
+    // Check we can retrieve it
+    int b = 0;
+    handle.getArgument("integer1", b);
+    EXPECT_EQ(42, b);
+
+    // Check that getting an unknown name throws an exception.
+    int c = 0;
+    EXPECT_THROW(handle.getArgument("unknown", c), NoSuchArgument);
+}
+
+// Test that trying to get an argument with an incorrect type throws an
+// exception.
+
+TEST_F(CalloutHandleTest, ArgumentIncorrectType) {
+    CalloutHandle handle(getCalloutManager());
+
+    // Set an integer
+    int a = 42;
+    handle.setArgument("integer1", a);
+    EXPECT_EQ(42, a);
+
+    // Check we can retrieve it
+    long b = 0;
+    EXPECT_THROW(handle.getArgument("integer1", b), boost::bad_any_cast);
+}
+
+// Now try with some very complex types.  The types cannot be defined within
+// the function and they should contain a copy constructor.  For this reason,
+// a simple "struct" is used.
+
+struct Alpha {
+    int a;
+    int b;
+    Alpha(int first = 0, int second = 0) : a(first), b(second) {}
+};
+
+struct Beta {
+    int c;
+    int d;
+    Beta(int first = 0, int second = 0) : c(first), d(second) {}
+};
+
+TEST_F(CalloutHandleTest, ComplexTypes) {
+    CalloutHandle handle(getCalloutManager());
+
+    // Declare two variables of different (complex) types. (Note as to the
+    // variable names: aleph and beth are the first two letters of the Hebrew
+    // alphabet.)
+    Alpha aleph(1, 2);
+    EXPECT_EQ(1, aleph.a);
+    EXPECT_EQ(2, aleph.b);
+    handle.setArgument("aleph", aleph);
+
+    Beta beth(11, 22);
+    EXPECT_EQ(11, beth.c);
+    EXPECT_EQ(22, beth.d);
+    handle.setArgument("beth", beth);
+
+    // Ensure we can extract the data correctly.
+    Alpha aleph2;
+    EXPECT_EQ(0, aleph2.a);
+    EXPECT_EQ(0, aleph2.b);
+    handle.getArgument("aleph", aleph2);
+    EXPECT_EQ(1, aleph2.a);
+    EXPECT_EQ(2, aleph2.b);
+
+    Beta beth2;
+    EXPECT_EQ(0, beth2.c);
+    EXPECT_EQ(0, beth2.d);
+    handle.getArgument("beth", beth2);
+    EXPECT_EQ(11, beth2.c);
+    EXPECT_EQ(22, beth2.d);
+
+    // Ensure that complex types also thrown an exception if we attempt to
+    // get a context element of the wrong type.
+    EXPECT_THROW(handle.getArgument("aleph", beth), boost::bad_any_cast);
+}
+
+// Check that the context can store pointers. And also check that it respects
+// that a "pointer to X" is not the same as a "pointer to const X".
+
+TEST_F(CalloutHandleTest, PointerTypes) {
+    CalloutHandle handle(getCalloutManager());
+
+    // Declare a couple of variables, const and non-const.
+    Alpha aleph(5, 10);
+    const Beta beth(15, 20);
+
+    Alpha* pa = &aleph;
+    const Beta* pcb = &beth;
+
+    // Check pointers can be set and retrieved OK.
+    handle.setArgument("non_const_pointer", pa);
+    handle.setArgument("const_pointer", pcb);
+
+    Alpha* pa2 = 0;
+    handle.getArgument("non_const_pointer", pa2);
+    EXPECT_TRUE(pa == pa2);
+
+    const Beta* pcb2 = 0;
+    handle.getArgument("const_pointer", pcb2);
+    EXPECT_TRUE(pcb == pcb2);
+
+    // Check that the "const" is protected in the context.
+    const Alpha* pca3;
+    EXPECT_THROW(handle.getArgument("non_const_pointer", pca3),
+                 boost::bad_any_cast);
+
+    Beta* pb3;
+    EXPECT_THROW(handle.getArgument("const_pointer", pb3),
+                 boost::bad_any_cast);
+}
+
+// Check that we can get the names of the arguments.
+
+TEST_F(CalloutHandleTest, ContextItemNames) {
+    CalloutHandle handle(getCalloutManager());
+
+    vector<string> expected_names;
+
+    expected_names.push_back("faith");
+    handle.setArgument("faith", 42);
+    expected_names.push_back("hope");
+    handle.setArgument("hope", 43);
+    expected_names.push_back("charity");
+    handle.setArgument("charity", 44);
+
+    // Get the names and check against the expected names.  We'll sort
+    // both arrays to simplify the checking.
+    vector<string> actual_names = handle.getArgumentNames();
+
+    sort(actual_names.begin(), actual_names.end());
+    sort(expected_names.begin(), expected_names.end());
+    EXPECT_TRUE(expected_names == actual_names);
+}
+
+// Test that we can delete an argument.
+
+TEST_F(CalloutHandleTest, DeleteArgument) {
+    CalloutHandle handle(getCalloutManager());
+
+    int one = 1;
+    int two = 2;
+    int three = 3;
+    int four = 4;
+    int value;      // Return value
+
+    handle.setArgument("one", one);
+    handle.setArgument("two", two);
+    handle.setArgument("three", three);
+    handle.setArgument("four", four);
+
+    // Delete "one".
+    handle.getArgument("one", value);
+    EXPECT_EQ(1, value);
+    handle.deleteArgument("one");
+
+    EXPECT_THROW(handle.getArgument("one", value), NoSuchArgument);
+    handle.getArgument("two", value);
+    EXPECT_EQ(2, value);
+    handle.getArgument("three", value);
+    EXPECT_EQ(3, value);
+    handle.getArgument("four", value);
+    EXPECT_EQ(4, value);
+
+    // Delete "three".
+    handle.getArgument("three", value);
+    EXPECT_EQ(3, value);
+    handle.deleteArgument("three");
+
+    EXPECT_THROW(handle.getArgument("one", value), NoSuchArgument);
+    handle.getArgument("two", value);
+    EXPECT_EQ(2, value);
+    EXPECT_THROW(handle.getArgument("three", value), NoSuchArgument);
+    handle.getArgument("four", value);
+    EXPECT_EQ(4, value);
+}
+
+// Test that we can delete all arguments.
+
+TEST_F(CalloutHandleTest, DeleteAllArguments) {
+    CalloutHandle handle(getCalloutManager());
+
+    int one = 1;
+    int two = 2;
+    int three = 3;
+    int four = 4;
+    int value;      // Return value
+
+    // Set the arguments.  The previous test verifies that this works.
+    handle.setArgument("one", one);
+    handle.setArgument("two", two);
+    handle.setArgument("three", three);
+    handle.setArgument("four", four);
+
+    // Delete all arguments...
+    handle.deleteAllArguments();
+
+    // ... and check that none are left.
+    EXPECT_THROW(handle.getArgument("one", value), NoSuchArgument);
+    EXPECT_THROW(handle.getArgument("two", value), NoSuchArgument);
+    EXPECT_THROW(handle.getArgument("three", value), NoSuchArgument);
+    EXPECT_THROW(handle.getArgument("four", value), NoSuchArgument);
+}
+
+// Test the "skip" flag.
+
+TEST_F(CalloutHandleTest, SkipFlag) {
+    CalloutHandle handle(getCalloutManager());
+
+    // Should be false on construction.
+    EXPECT_FALSE(handle.getSkip());
+
+    handle.setSkip(true);
+    EXPECT_TRUE(handle.getSkip());
+
+    handle.setSkip(false);
+    EXPECT_FALSE(handle.getSkip());
+}
+
+} // Anonymous namespace

+ 765 - 0
src/lib/util/tests/callout_manager_unittest.cc

@@ -0,0 +1,765 @@
+// 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 <exceptions/exceptions.h>
+#include <util/hooks/callout_handle.h>
+#include <util/hooks/callout_manager.h>
+#include <util/hooks/library_handle.h>
+#include <util/hooks/server_hooks.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+/// @file
+/// @brief CalloutManager and LibraryHandle tests
+///
+/// These set of tests check the CalloutManager and LibraryHandle.  They are
+/// together in the same file because the LibraryHandle is little more than a
+/// restricted interface to the CalloutManager, and a lot of the support
+/// structure for the tests is common.
+
+using namespace isc;
+using namespace isc::util;
+using namespace std;
+
+namespace {
+
+class CalloutManagerTest : public ::testing::Test {
+public:
+    /// @brief Constructor
+    ///
+    /// Sets up a collection of three LibraryHandle objects to use in the test.
+    CalloutManagerTest() : hooks_(new ServerHooks()) {
+
+        // Set up the server hooks
+        alpha_index_ = hooks_->registerHook("alpha");
+        beta_index_ = hooks_->registerHook("beta");
+        gamma_index_ = hooks_->registerHook("gamma");
+        delta_index_ = hooks_->registerHook("delta");
+
+        // Set up the callout manager with these hooks.  Assume a maximum of
+        // four libraries.
+        callout_manager_.reset(new CalloutManager(hooks_, 10));
+
+        // Set up the callout handle.
+        callout_handle_.reset(new CalloutHandle(callout_manager_));
+
+        // Initialize the static variable.
+        callout_value_ = 0;
+    }
+
+    /// @brief Return the callout handle
+    CalloutHandle& getCalloutHandle() {
+        return (*callout_handle_);
+    }
+
+    /// @brief Return the callout manager
+    boost::shared_ptr<CalloutManager> getCalloutManager() {
+        return (callout_manager_);
+    }
+
+    boost::shared_ptr<ServerHooks> getServerHooks() {
+        return (hooks_);
+    }
+
+    /// Static variable used for accumulating information
+    static int callout_value_;
+
+    /// Hook indexes.  These are somewhat ubiquitous, so are made public for
+    /// ease of reference instead of being accessible by a function.
+    int alpha_index_;
+    int beta_index_;
+    int gamma_index_;
+    int delta_index_;
+
+private:
+    /// Callout handle used in calls
+    boost::shared_ptr<CalloutHandle> callout_handle_;
+
+    /// Callout manager used for the test
+    boost::shared_ptr<CalloutManager> callout_manager_;
+
+    /// Server hooks
+    boost::shared_ptr<ServerHooks> hooks_;
+};
+
+// Definition of the static variable.
+int CalloutManagerTest::callout_value_ = 0;
+
+// Callout definitions
+//
+// The callouts defined here are structured in such a way that it is possible
+// to determine the order in which they are called and whether they are called
+// at all. The method used is simple - after a sequence of callouts, the digits
+// in the value, reading left to right, determines the order of the callouts
+// called.  For example, callout one followed by two followed by three followed
+// by two followed by one results in a value of 12321.
+//
+// Functions return a zero to indicate success.
+
+extern "C" {
+int callout_general(int number) {
+    CalloutManagerTest::callout_value_ =
+        10 * CalloutManagerTest::callout_value_ + number;
+    return (0);
+}
+
+int callout_one(CalloutHandle&) {
+    return (callout_general(1));
+}
+
+int callout_two(CalloutHandle&) {
+    return (callout_general(2));
+}
+
+int callout_three(CalloutHandle&) {
+    return (callout_general(3));
+}
+
+int callout_four(CalloutHandle&) {
+    return (callout_general(4));
+}
+
+int callout_five(CalloutHandle&) {
+    return (callout_general(5));
+}
+
+int callout_six(CalloutHandle&) {
+    return (callout_general(6));
+}
+
+int callout_seven(CalloutHandle&) {
+    return (callout_general(7));
+}
+
+// The next functions are duplicates of some of the above, but return an error.
+
+int callout_one_error(CalloutHandle& handle) {
+    (void) callout_one(handle);
+    return (1);
+}
+
+int callout_two_error(CalloutHandle& handle) {
+    (void) callout_two(handle);
+    return (1);
+}
+
+int callout_three_error(CalloutHandle& handle) {
+    (void) callout_three(handle);
+    return (1);
+}
+
+int callout_four_error(CalloutHandle& handle) {
+    (void) callout_four(handle);
+    return (1);
+}
+
+};  // extern "C"
+
+// *** Callout Tests ***
+//
+// The next set of tests check that callouts can be called.
+
+// Constructor - check that we trap bad parameters.
+
+TEST_F(CalloutManagerTest, BadConstructorParameters) {
+    boost::scoped_ptr<CalloutManager> cm;
+
+    // Invalid number of libraries
+    EXPECT_THROW(cm.reset(new CalloutManager(getServerHooks(), 0)), BadValue);
+    EXPECT_THROW(cm.reset(new CalloutManager(getServerHooks(), -1)), BadValue);
+
+    // Invalid server hooks pointer.
+    boost::shared_ptr<ServerHooks> sh;
+    EXPECT_THROW(cm.reset(new CalloutManager(sh, 4)), BadValue);
+}
+
+// Check the number of libraries is reported successfully.
+
+TEST_F(CalloutManagerTest, GetNumLibraries) {
+    boost::scoped_ptr<CalloutManager> cm;
+
+    // Check two valid values of number of libraries to ensure that the
+    // GetNumLibraries() returns the value set.
+    EXPECT_NO_THROW(cm.reset(new CalloutManager(getServerHooks(), 4)));
+    EXPECT_EQ(4, cm->getNumLibraries());
+
+    EXPECT_NO_THROW(cm.reset(new CalloutManager(getServerHooks(), 42)));
+    EXPECT_EQ(42, cm->getNumLibraries());
+}
+
+// Check that we can only set the current library index to the correct values.
+
+TEST_F(CalloutManagerTest, CheckLibraryIndex) {
+    // Check valid indexes
+    for (int i = 0; i < 4; ++i) {
+        EXPECT_NO_THROW(getCalloutManager()->setLibraryIndex(i));
+    }
+
+    // Check invalid ones
+    EXPECT_THROW(getCalloutManager()->setLibraryIndex(-1), NoSuchLibrary);
+    EXPECT_THROW(getCalloutManager()->setLibraryIndex(15), NoSuchLibrary);
+}
+
+// Check that we can only register callouts on valid hook names.
+
+TEST_F(CalloutManagerTest, ValidHookNames) {
+    getCalloutManager()->setLibraryIndex(0);
+    EXPECT_NO_THROW(getCalloutManager()->registerCallout("alpha", callout_one));
+    EXPECT_THROW(getCalloutManager()->registerCallout("unknown", callout_one),
+                                                      NoSuchHook);
+}
+
+
+// Check we can register callouts appropriately.
+
+TEST_F(CalloutManagerTest, RegisterCallout) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+
+    // Set up so that hooks "alpha" and "beta" have callouts attached from a
+    // different libraries.
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout_one);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("beta", callout_two);
+
+    // Check all is as expected.
+    EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_TRUE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+                 
+    // Check that calling the callouts returns as expected. (This is also a
+    // test of the callCallouts method.)
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1, callout_value_);
+
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
+    EXPECT_EQ(2, callout_value_);
+
+    // Register some more callouts from different libraries on hook "alpha".
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", callout_three);
+    getCalloutManager()->registerCallout("alpha", callout_four);
+    getCalloutManager()->setLibraryIndex(3);
+    getCalloutManager()->registerCallout("alpha", callout_five);
+
+    // Check it is as expected.
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1345, callout_value_);
+
+    // ... and check the additional callouts were not registered on the "beta"
+    // hook.
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
+    EXPECT_EQ(2, callout_value_);
+
+    // Add another callout to hook "alpha" from library index 2 - this should
+    // appear at the end of the callout list for that library.
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", callout_six);
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(13465, callout_value_);
+
+    // Add a callout from library index 1 - this should appear between the
+    // callouts from library index 0 and linrary index 2.
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout_seven);
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(173465, callout_value_);
+}
+
+// Check the "calloutsPresent()" method.
+
+TEST_F(CalloutManagerTest, CalloutsPresent) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+                 
+    // Set up so that hooks "alpha", "beta" and "delta" have callouts attached
+    // to them, and callout  "gamma" does not. (In the statements below, the
+    // exact callouts attached to a hook are not relevant - only the fact
+    // that some callouts are).  Chose the libraries for which the callouts
+    // are registered randomly.
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout_one);
+
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->registerCallout("beta", callout_two);
+
+    getCalloutManager()->setLibraryIndex(3);
+    getCalloutManager()->registerCallout("alpha", callout_three);
+    getCalloutManager()->registerCallout("delta", callout_four);
+
+    // Check all is as expected.
+    EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_TRUE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_TRUE(getCalloutManager()->calloutsPresent(delta_index_));
+
+    // Check we fail on an invalid hook index.
+    EXPECT_THROW(getCalloutManager()->calloutsPresent(42), NoSuchHook);
+    EXPECT_THROW(getCalloutManager()->calloutsPresent(-1), NoSuchHook);
+}
+
+// Test that calling a hook with no callouts on it returns success.
+
+TEST_F(CalloutManagerTest, CallNoCallouts) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+                 
+    // Call the callouts on an arbitrary hook and ensure that nothing happens.
+    callout_value_ = 475;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(475, callout_value_); // Unchanged
+}
+
+// Test that the callouts are called in the correct order (i.e. the callouts
+// from the first library in the order they were registered, then the callouts
+// from the second library in the order they were registered etc.)
+
+TEST_F(CalloutManagerTest, CallCalloutsSuccess) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+                 
+    // Each library contributes one callout on hook "alpha".
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout_one);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", callout_three);
+    getCalloutManager()->setLibraryIndex(3);
+    getCalloutManager()->registerCallout("alpha", callout_four);
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1234, callout_value_);
+
+    // Do a random selection of callouts on hook "beta".
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("beta", callout_one);
+    getCalloutManager()->registerCallout("beta", callout_three);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("beta", callout_two);
+    getCalloutManager()->setLibraryIndex(3);
+    getCalloutManager()->registerCallout("beta", callout_four);
+    getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
+    EXPECT_EQ(1324, callout_value_);
+
+    // Ensure that calling the callouts on a hook with no callouts works.
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(gamma_index_, getCalloutHandle());
+    EXPECT_EQ(0, callout_value_);
+}
+
+// Test that the callouts are called in order, but that callouts occurring
+// after a callout that returns an error are not called.
+//
+// (Note: in this test, the callouts that return an error set the value of
+// callout_value_ before they return the error code.)
+
+TEST_F(CalloutManagerTest, CallCalloutsError) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+                 
+    // Each library contributing one callout on hook "alpha". The first callout
+    // returns an error (after adding its value to the result).
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout_one_error);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", callout_three);
+    getCalloutManager()->setLibraryIndex(3);
+    getCalloutManager()->registerCallout("alpha", callout_four);
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1234, callout_value_);
+
+    // Each library contributing multiple callouts on hook "beta". The last
+    // callout on the first library returns an error.
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("beta", callout_one);
+    getCalloutManager()->registerCallout("beta", callout_one_error);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("beta", callout_two);
+    getCalloutManager()->registerCallout("beta", callout_two);
+    getCalloutManager()->registerCallout("beta", callout_three);
+    getCalloutManager()->registerCallout("beta", callout_three);
+    getCalloutManager()->setLibraryIndex(3);
+    getCalloutManager()->registerCallout("beta", callout_four);
+    getCalloutManager()->registerCallout("beta", callout_four);
+    getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
+    EXPECT_EQ(11223344, callout_value_);
+
+    // A callout in a random position in the callout list returns an error.
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("gamma", callout_one);
+    getCalloutManager()->registerCallout("gamma", callout_one);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("gamma", callout_two);
+    getCalloutManager()->registerCallout("gamma", callout_two);
+    getCalloutManager()->setLibraryIndex(3);
+    getCalloutManager()->registerCallout("gamma", callout_four_error);
+    getCalloutManager()->registerCallout("gamma", callout_four);
+    getCalloutManager()->callCallouts(gamma_index_, getCalloutHandle());
+    EXPECT_EQ(112244, callout_value_);
+
+    // The last callout on a hook returns an error.
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("delta", callout_one);
+    getCalloutManager()->registerCallout("delta", callout_one);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("delta", callout_two);
+    getCalloutManager()->registerCallout("delta", callout_two);
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("delta", callout_three);
+    getCalloutManager()->registerCallout("delta", callout_three);
+    getCalloutManager()->setLibraryIndex(3);
+    getCalloutManager()->registerCallout("delta", callout_four);
+    getCalloutManager()->registerCallout("delta", callout_four_error);
+    getCalloutManager()->callCallouts(delta_index_, getCalloutHandle());
+    EXPECT_EQ(11223344, callout_value_);
+}
+
+// Now test that we can deregister a single callout on a hook.
+
+TEST_F(CalloutManagerTest, DeregisterSingleCallout) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+                 
+    // Add a callout to hook "alpha" and check it is added correctly.
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(2, callout_value_);
+
+    // Remove it and check that the no callouts are present.  We have to reset
+    // the current library index here as it was invalidated by the call
+    // to callCallouts().
+    getCalloutManager()->setLibraryIndex(0);
+    EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+}
+
+// Now test that we can deregister a single callout on a hook that has multiple
+// callouts from the same library.
+
+TEST_F(CalloutManagerTest, DeregisterSingleCalloutSameLibrary) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+                 
+    // Add multiple callouts to hook "alpha".
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout_one);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->registerCallout("alpha", callout_three);
+    getCalloutManager()->registerCallout("alpha", callout_four);
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1234, callout_value_);
+
+    // Remove the callout_two callout.  We have to reset the current library
+    // index here as it was invalidated by the call to callCallouts().
+    getCalloutManager()->setLibraryIndex(0);
+    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(134, callout_value_);
+
+    // Try removing it again.
+    getCalloutManager()->setLibraryIndex(0);
+    EXPECT_FALSE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(134, callout_value_);
+
+}
+
+// Check we can deregister multiple callouts from the same library.
+
+TEST_F(CalloutManagerTest, DeregisterMultipleCalloutsSameLibrary) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+                 
+    // Each library contributes one callout on hook "alpha".
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout_one);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->registerCallout("alpha", callout_one);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->registerCallout("alpha", callout_three);
+    getCalloutManager()->registerCallout("alpha", callout_four);
+    getCalloutManager()->registerCallout("alpha", callout_three);
+    getCalloutManager()->registerCallout("alpha", callout_four);
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(12123434, callout_value_);
+
+    // Remove the callout_two callouts.  We have to reset the current library
+    // index here as it was invalidated by the call to callCallouts().
+    getCalloutManager()->setLibraryIndex(0);
+    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(113434, callout_value_);
+
+    // Try removing multiple callouts that includes one at the end of the
+    // list of callouts.
+    getCalloutManager()->setLibraryIndex(0);
+    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_four));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1133, callout_value_);
+
+    // ... and from the start.
+    getCalloutManager()->setLibraryIndex(0);
+    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_one));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(33, callout_value_);
+
+    // ... and the remaining callouts.
+    getCalloutManager()->setLibraryIndex(0);
+    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_three));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(0, callout_value_);
+
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+}
+
+// Check we can deregister multiple callouts from multiple libraries.
+
+TEST_F(CalloutManagerTest, DeregisterMultipleCalloutsMultipleLibraries) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+                 
+    // Each library contributes two callouts to hook "alpha".
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout_one);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout_three);
+    getCalloutManager()->registerCallout("alpha", callout_four);
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", callout_five);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(123452, callout_value_);
+
+    // Remove the callout_two callout from library 0.  It should not affect
+    // the second callout_two callout registered by library 2.
+    getCalloutManager()->setLibraryIndex(0);
+    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(13452, callout_value_);
+}
+
+// Check we can deregister all callouts from a single library.
+
+TEST_F(CalloutManagerTest, DeregisterAllCallouts) {
+    // Ensure that no callouts are attached to hook one.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+                 
+    // Each library contributes two callouts to hook "alpha".
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout_one);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout_three);
+    getCalloutManager()->registerCallout("alpha", callout_four);
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", callout_five);
+    getCalloutManager()->registerCallout("alpha", callout_six);
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(123456, callout_value_);
+
+    // Remove all callouts from library index 1.
+    getCalloutManager()->setLibraryIndex(1);
+    EXPECT_TRUE(getCalloutManager()->deregisterAllCallouts("alpha"));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1256, callout_value_);
+
+    // Remove all callouts from library index 2.
+    getCalloutManager()->setLibraryIndex(2);
+    EXPECT_TRUE(getCalloutManager()->deregisterAllCallouts("alpha"));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(12, callout_value_);
+}
+
+// Check that we can register/deregister callouts on different libraries
+// and different hooks, and that the callout instances are regarded as
+// unique and do not affect one another.
+
+TEST_F(CalloutManagerTest, MultipleCalloutsLibrariesHooks) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+                 
+    // Register callouts on the alpha hook.
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout_one);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout_three);
+    getCalloutManager()->registerCallout("alpha", callout_four);
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", callout_five);
+    getCalloutManager()->registerCallout("alpha", callout_two);
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(123452, callout_value_);
+
+    // Register the same callouts on the beta hook, and check that those
+    // on the alpha hook are not affected.
+    callout_value_ = 0;
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("beta", callout_five);
+    getCalloutManager()->registerCallout("beta", callout_one);
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("beta", callout_four);
+    getCalloutManager()->registerCallout("beta", callout_three);
+    getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
+    EXPECT_EQ(5143, callout_value_);
+
+    // Check that the order of callouts on the alpha hook has not been affected.
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(123452, callout_value_);
+
+    // Remove callout four from beta and check that alpha is not affected.
+    getCalloutManager()->setLibraryIndex(2);
+    EXPECT_TRUE(getCalloutManager()->deregisterCallout("beta", callout_four));
+
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
+    EXPECT_EQ(513, callout_value_);
+
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(123452, callout_value_);
+}
+
+// Library handle tests.  As by inspection the LibraryHandle can be seen to be
+// little more than shell around CalloutManager, only a basic set of tests
+// is done concerning registration and deregistration of functions.
+//
+// More extensive tests (i.e. checking that when a callout is called it can
+// only register and deregister callouts within its library) require that
+// the CalloutHandle object pass the appropriate LibraryHandle to the
+// callout.  These tests are done in the handles_unittest tests.
+
+TEST_F(CalloutManagerTest, LibraryHandleRegistration) {
+    // Ensure that no callouts are attached to any of the hooks.
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+
+    // Set up so that hooks "alpha" and "beta" have callouts attached from a
+    // different libraries.
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->getLibraryHandle().registerCallout("alpha",
+                                                            callout_one);
+    getCalloutManager()->getLibraryHandle().registerCallout("alpha",
+                                                            callout_two);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->getLibraryHandle().registerCallout("alpha",
+                                                            callout_three);
+    getCalloutManager()->getLibraryHandle().registerCallout("alpha",
+                                                            callout_four);
+
+    // Check all is as expected.
+    EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+    EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+                 
+    // Check that calling the callouts returns as expected. (This is also a
+    // test of the callCallouts method.)
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1234, callout_value_);
+
+    // Deregister a callout on library index 0 (after we check we can't
+    // deregister it through library index 1).
+    getCalloutManager()->setLibraryIndex(1);
+    EXPECT_FALSE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1234, callout_value_);
+
+    getCalloutManager()->setLibraryIndex(0);
+    EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(134, callout_value_);
+
+    // Deregister all callouts on library index 1.
+    getCalloutManager()->setLibraryIndex(1);
+    EXPECT_TRUE(getCalloutManager()->deregisterAllCallouts("alpha"));
+    callout_value_ = 0;
+    getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+    EXPECT_EQ(1, callout_value_);
+}
+
+
+
+} // Anonymous namespace

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

@@ -0,0 +1,922 @@
+// 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/callout_manager.h>
+#include <util/hooks/library_handle.h>
+#include <util/hooks/server_hooks.h>
+
+#include <boost/lexical_cast.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <string>
+
+/// @file
+/// CalloutHandle/LibraryHandle interaction tests
+///
+/// This file holds unit tests checking the interaction between the
+/// CalloutHandle/LibraryHandle and CalloutManager classes.  In particular,
+/// they check that:
+///
+/// - A CalloutHandle's context is shared between callouts from the same
+///   library, but there is a separate context for each library.
+///
+/// - The various methods manipulating the items in the CalloutHandle's context
+///   work correctly.
+///
+/// - An active callout can only modify the registration of callouts registered
+///   by its own library.
+
+using namespace isc::util;
+using namespace std;
+
+namespace {
+
+class HandlesTest : public ::testing::Test {
+public:
+    /// @brief Constructor
+    ///
+    /// Sets up the various elements used in each test.
+    HandlesTest() : hooks_(new ServerHooks()), manager_()
+    {
+        // Set up four hooks, although through gamma
+        alpha_index_ = hooks_->registerHook("alpha");
+        beta_index_ = hooks_->registerHook("beta");
+        gamma_index_ = hooks_->registerHook("gamma");
+        delta_index_ = hooks_->registerHook("delta");
+
+        // Set up for three libraries.
+        manager_.reset(new CalloutManager(hooks_, 3));
+
+        // Initialize remaining variables.
+        common_string_ = "";
+    }
+
+    /// @brief Return callout manager
+    boost::shared_ptr<CalloutManager> getCalloutManager() {
+        return (manager_);
+    }
+
+    /// Hook indexes - these are frequently accessed, so are accessed directly.
+    int alpha_index_;
+    int beta_index_;
+    int gamma_index_;
+    int delta_index_;
+
+    /// String accessible by all callouts whatever the library
+    static std::string common_string_;
+
+private:
+    /// Server hooks 
+    boost::shared_ptr<ServerHooks> hooks_;
+
+    /// Callout manager.  Declared static so that the callout functions can
+    /// access it.
+    boost::shared_ptr<CalloutManager> manager_;
+};
+
+/// Define the common string
+std::string HandlesTest::common_string_;
+
+
+// The next set of functions define the callouts used by the tests.  They
+// manipulate the data in such a way that callouts called - and the order in
+// which they were called - can be determined.  The functions also check that
+// the "callout context" data areas are separate.
+//
+// Three libraries are assumed, and each supplies four callouts.  All callouts
+// manipulate two context elements the CalloutHandle, the elements being called
+// "string" and "int" (which describe the type of data manipulated).
+//
+// For the string item, each callout shifts data to the left and inserts its own
+// data.  The data is a string of the form "nmc", where "n" is the number of
+// the library, "m" is the callout number and "y" is the indication of what
+// callout handle was passed as an argument ("1" or "2": "0" is used when no
+// identification has been set in the callout handle).
+//
+// For simplicity, and to cut down the number of functions actually written,
+// the callout indicator ("1" or "2") ) 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.
+//
+// For integer data, the value starts at zero and an increment is added on each
+// call.  This increment is equal to:
+//
+// 100 * library number + 10 * callout number + callout handle
+//
+// Although this gives less information than the string value, the reasons for
+// using it are:
+//
+// - It is a separate item in the context, so checks that the context can
+//   handle multiple items.
+// - It provides an item that can be deleted by the context deletion
+//   methods.
+
+
+// Values set in the CalloutHandle context.  There are three libraries, so
+// there are three contexts for the callout, one for each library.
+
+std::string& resultCalloutString(int index) {
+    static std::string result_callout_string[3];
+    return (result_callout_string[index]);
+}
+
+int& resultCalloutInt(int index) {
+    static int result_callout_int[3];
+    return (result_callout_int[index]);
+}
+
+// A simple function to zero the results.
+
+static void zero_results() {
+    for (int i = 0; i < 3; ++i) {
+        resultCalloutString(i) = "";
+        resultCalloutInt(i) = 0;
+    }
+}
+
+
+// Library callouts.
+
+// Common code for setting the callout context values.
+
+int
+execute(CalloutHandle& callout_handle, int library_num, int callout_num) {
+
+    // Obtain the callout handle number
+    int handle_num = 0;
+    try  {
+        callout_handle.getArgument("handle_num", handle_num);
+    } catch (const NoSuchArgument&) {
+        // handle_num argument not set: this is the case in the tests where
+        // the context_create hook check is tested.
+        handle_num = 0;
+    }
+
+    // Create the basic data to be appended to the context value.
+    int idata = 100 * library_num + 10 * callout_num + handle_num;
+    string sdata = boost::lexical_cast<string>(idata);
+
+    // 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.)
+    int int_value = 0;
+    try {
+        callout_handle.getContext("int", int_value);
+    } catch (const NoSuchCalloutContext&) {
+        int_value = 0;
+    }
+
+    string string_value = "";
+    try {
+        callout_handle.getContext("string", string_value);
+    } catch (const NoSuchCalloutContext&) {
+        string_value = "";
+    }
+
+    // Update the values and set them back in the callout context.
+    int_value += idata;
+    callout_handle.setContext("int", int_value);
+
+    string_value += sdata;
+    callout_handle.setContext("string", string_value);
+
+    return (0);
+}
+
+// The following functions are the actual callouts - the name is of the
+// form "callout_<library number>_<callout number>"
+
+int
+callout11(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 1, 1));
+}
+
+int
+callout12(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 1, 2));
+}
+
+int
+callout13(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 1, 3));
+}
+
+int
+callout21(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 2, 1));
+}
+
+int
+callout22(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 2, 2));
+}
+
+int
+callout23(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 2, 3));
+}
+
+int
+callout31(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 3, 1));
+}
+
+int
+callout32(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 3, 2));
+}
+
+int
+callout33(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 printExecute(CalloutHandle& callout_handle, int library_num) {
+    callout_handle.getContext("string", resultCalloutString(library_num - 1));
+    callout_handle.getContext("int", resultCalloutInt(library_num - 1));
+
+    return (0);
+}
+
+// These are the actual callouts.
+
+int
+print1(CalloutHandle& callout_handle) {
+    return (printExecute(callout_handle, 1));
+}
+
+int
+print2(CalloutHandle& callout_handle) {
+    return (printExecute(callout_handle, 2));
+}
+
+int
+print3(CalloutHandle& callout_handle) {
+    return (printExecute(callout_handle, 3));
+}
+
+// This test checks the many-faced nature of the context for the CalloutContext.
+
+TEST_F(HandlesTest, ContextAccessCheck) {
+    // Register callouts for the different libraries.
+    CalloutHandle handle(getCalloutManager());
+
+    // Library 0.
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout11);
+    getCalloutManager()->registerCallout("beta", callout12);
+    getCalloutManager()->registerCallout("gamma", callout13);
+    getCalloutManager()->registerCallout("delta", print1);
+
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout21);
+    getCalloutManager()->registerCallout("beta", callout22);
+    getCalloutManager()->registerCallout("gamma", callout23);
+    getCalloutManager()->registerCallout("delta", print2);
+
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", callout31);
+    getCalloutManager()->registerCallout("beta", callout32);
+    getCalloutManager()->registerCallout("gamma", callout33);
+    getCalloutManager()->registerCallout("delta", print3);
+
+    // Create the callout handles and distinguish them by setting the
+    // "handle_num" argument.
+    CalloutHandle callout_handle_1(getCalloutManager());
+    callout_handle_1.setArgument("handle_num", static_cast<int>(1)); 
+
+    CalloutHandle callout_handle_2(getCalloutManager());
+    callout_handle_2.setArgument("handle_num", static_cast<int>(2)); 
+
+    // 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.
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
+    getCalloutManager()->callCallouts(beta_index_, callout_handle_1);
+    getCalloutManager()->callCallouts(beta_index_, callout_handle_2);
+    getCalloutManager()->callCallouts(gamma_index_, callout_handle_1);
+    getCalloutManager()->callCallouts(gamma_index_, callout_handle_2);
+
+    // Get the results for each callout (the callout on hook "delta" copies
+    // the context values into a location the test can access).  Explicitly
+    // zero the variables before getting the results so we are certain that
+    // the values are the results of the callouts.
+
+    zero_results();
+
+    // To explain the expected callout context results.
+    //
+    // Each callout handle maintains a separate context for each library.  When
+    // the first call to callCallouts() is made, "111" gets appended to
+    // the context for library 1 maintained by the first callout handle, "211"
+    // gets appended to the context maintained for library 2, and "311" to
+    // the context maintained for library 3.  In each case, the first digit
+    // corresponds to the library number, the second to the callout number and
+    // the third to the "handle_num" of the callout handle. For the first call
+    // to callCallouts, handle 1 is used, so the last digit is always 1.
+    //
+    // The next call to callCallouts() calls the same callouts but for the
+    // second callout handle.  It also maintains three contexts (one for
+    // each library) and they will get "112", "212", "312" appended to
+    // them. The explanation for the digits is the same as before, except that
+    // in this case, the callout handle is number 2, so the third digit is
+    // always 2.  These additions don't affect the contexts maintained by
+    // callout handle 1.
+    //
+    // The process is then repeated for hooks "beta" and "gamma" which, for
+    // callout handle 1, append "121", "221" and "321" for hook "beta" and
+    // "311", "321" and "331" for hook "gamma".
+    //
+    // The expected integer values can be found by summing up the values
+    // corresponding to the elements of the strings.
+
+    // At this point, we have only called the "print" function for callout
+    // handle "1", so the following results are checking the context values
+    // maintained in that callout handle.
+
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
+    EXPECT_EQ("111121131", resultCalloutString(0));
+    EXPECT_EQ("211221231", resultCalloutString(1));
+    EXPECT_EQ("311321331", resultCalloutString(2));
+
+    EXPECT_EQ((111 + 121 + 131), resultCalloutInt(0));
+    EXPECT_EQ((211 + 221 + 231), resultCalloutInt(1));
+    EXPECT_EQ((311 + 321 + 331), resultCalloutInt(2));
+
+    // Repeat the checks for callout 2.
+
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
+
+    EXPECT_EQ((112 + 122 + 132), resultCalloutInt(0));
+    EXPECT_EQ((212 + 222 + 232), resultCalloutInt(1));
+    EXPECT_EQ((312 + 322 + 332), resultCalloutInt(2));
+
+    EXPECT_EQ("112122132", resultCalloutString(0));
+    EXPECT_EQ("212222232", resultCalloutString(1));
+    EXPECT_EQ("312322332", resultCalloutString(2));
+}
+
+// Now repeat the test, but add a deletion callout to the list.  The "beta"
+// hook of library 2 will have an additional callout to delete the "int"
+// element: the same hook for library 3 will delete both elements.  In
+// addition, the names of context elements for the libraries at this point
+// will be printed.
+
+// List of context item names.
+
+vector<string>&
+getItemNames(int index) {
+    static vector<string> context_items[3];
+    return (context_items[index]);
+}
+
+// Context item deletion functions.
+
+int
+deleteIntContextItem(CalloutHandle& handle) {
+    handle.deleteContext("int");
+    return (0);
+}
+
+int
+deleteAllContextItems(CalloutHandle& handle) {
+    handle.deleteAllContext();
+    return (0);
+}
+
+// Generic print function - copy names in sorted order.
+
+int
+printContextNamesExecute(CalloutHandle& handle, int library_num) {
+    const int index = library_num - 1;
+    getItemNames(index) = handle.getContextNames();
+    sort(getItemNames(index).begin(), getItemNames(index).end());
+    return (0);
+}
+
+int
+printContextNames1(CalloutHandle& handle) {
+    return (printContextNamesExecute(handle, 1));
+}
+
+int
+printContextNames2(CalloutHandle& handle) {
+    return (printContextNamesExecute(handle, 2));
+}
+
+int
+printContextNames3(CalloutHandle& handle) {
+    return (printContextNamesExecute(handle, 3));
+}
+
+// Perform the test including deletion of context items.
+
+TEST_F(HandlesTest, ContextDeletionCheck) {
+
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout11);
+    getCalloutManager()->registerCallout("beta", callout12);
+    getCalloutManager()->registerCallout("beta", printContextNames1);
+    getCalloutManager()->registerCallout("gamma", callout13);
+    getCalloutManager()->registerCallout("delta", print1);
+
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout21);
+    getCalloutManager()->registerCallout("beta", callout22);
+    getCalloutManager()->registerCallout("beta", deleteIntContextItem);
+    getCalloutManager()->registerCallout("beta", printContextNames2);
+    getCalloutManager()->registerCallout("gamma", callout23);
+    getCalloutManager()->registerCallout("delta", print2);
+
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", callout31);
+    getCalloutManager()->registerCallout("beta", callout32);
+    getCalloutManager()->registerCallout("beta", deleteAllContextItems);
+    getCalloutManager()->registerCallout("beta", printContextNames3);
+    getCalloutManager()->registerCallout("gamma", callout33);
+    getCalloutManager()->registerCallout("delta", print3);
+
+    // Create the callout handles and distinguish them by setting the "long"
+    // argument.
+    CalloutHandle callout_handle_1(getCalloutManager());
+    callout_handle_1.setArgument("handle_num", static_cast<int>(1));
+
+    CalloutHandle callout_handle_2(getCalloutManager());
+    callout_handle_2.setArgument("handle_num", static_cast<int>(2));
+
+    // 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.
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
+    getCalloutManager()->callCallouts(beta_index_, callout_handle_1);
+    getCalloutManager()->callCallouts(beta_index_, callout_handle_2);
+    getCalloutManager()->callCallouts(gamma_index_, callout_handle_1);
+    getCalloutManager()->callCallouts(gamma_index_, callout_handle_2);
+
+    // 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();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
+
+    // The logic by which the expected results are arrived at is described
+    // in the ContextAccessCheck test.  The results here are different
+    // because context items have been modified along the way.
+
+    EXPECT_EQ((111 + 121 + 131), resultCalloutInt(0));
+    EXPECT_EQ((            231), resultCalloutInt(1));
+    EXPECT_EQ((            331), resultCalloutInt(2));
+
+    EXPECT_EQ("111121131", resultCalloutString(0));
+    EXPECT_EQ("211221231", resultCalloutString(1));
+    EXPECT_EQ(      "331", resultCalloutString(2));
+
+    // Repeat the checks for callout handle 2.
+
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
+
+    EXPECT_EQ((112 + 122 + 132), resultCalloutInt(0));
+    EXPECT_EQ((            232), resultCalloutInt(1));
+    EXPECT_EQ((            332), resultCalloutInt(2));
+
+    EXPECT_EQ("112122132", resultCalloutString(0));
+    EXPECT_EQ("212222232", resultCalloutString(1));
+    EXPECT_EQ(      "332", resultCalloutString(2));
+
+    // ... and check what the names of the context items are after the callouts
+    // for hook "beta".  We know they are in sorted order.
+
+    EXPECT_EQ(2, getItemNames(0).size());
+    EXPECT_EQ(string("int"),    getItemNames(0)[0]);
+    EXPECT_EQ(string("string"), getItemNames(0)[1]);
+
+    EXPECT_EQ(1, getItemNames(1).size());
+    EXPECT_EQ(string("string"), getItemNames(1)[0]);
+
+    EXPECT_EQ(0, getItemNames(2).size());
+}
+
+// Tests that the CalloutHandle's constructor and destructor call the
+// context_create and context_destroy callbacks (if registered).  For
+// simplicity, we'll use the same callout functions as used above, plus
+// the following that returns an error:
+
+int returnError(CalloutHandle&) {
+    return (1);
+}
+
+TEST_F(HandlesTest, ConstructionDestructionCallouts) {
+
+    // Register context callouts.
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("context_create", callout11);
+    getCalloutManager()->registerCallout("context_create", print1);
+    getCalloutManager()->registerCallout("context_destroy", callout12);
+    getCalloutManager()->registerCallout("context_destroy", print1);
+
+    // Create the CalloutHandle and check that the constructor callout
+    // has run.
+    zero_results();
+    boost::scoped_ptr<CalloutHandle>
+        callout_handle(new CalloutHandle(getCalloutManager()));
+    EXPECT_EQ("110", resultCalloutString(0));
+    EXPECT_EQ(110, resultCalloutInt(0));
+
+    // Check that the destructor callout runs.  Note that the "print1" callout
+    // didn't destroy the library context - it only copied it to where it
+    // could be examined.  As a result, the destructor callout appends its
+    // elements to the constructor's values and the result is printed.
+    zero_results();
+    callout_handle.reset();
+
+    EXPECT_EQ("110120", resultCalloutString(0));
+    EXPECT_EQ((110 + 120), resultCalloutInt(0));
+}
+
+// Dynamic callout registration and deregistration.
+// The following are the dynamic registration/deregistration callouts.
+
+
+// Add callout_78_alpha - adds a callout to hook alpha that appends "78x"
+// (where "x" is the callout handle) to the current output.
+
+int
+callout78(CalloutHandle& callout_handle) {
+    return (execute(callout_handle, 7, 8));
+}
+
+int
+add_callout78_alpha(CalloutHandle& callout_handle) {
+    callout_handle.getLibraryHandle().registerCallout("alpha", callout78);
+    return (0);
+}
+
+int
+delete_callout78_alpha(CalloutHandle& callout_handle) {
+    static_cast<void>(
+        callout_handle.getLibraryHandle().deregisterCallout("alpha",
+                                                            callout78));
+    return (0);
+}
+
+// Check that a callout can register another callout on a different hook.
+
+TEST_F(HandlesTest, DynamicRegistrationAnotherHook) {
+    // Register callouts for the different libraries.
+    CalloutHandle handle(getCalloutManager());
+
+    // Set up callouts on "alpha".
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout11);
+    getCalloutManager()->registerCallout("delta", print1);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout21);
+    getCalloutManager()->registerCallout("delta", print2);
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", callout31);
+    getCalloutManager()->registerCallout("delta", print3);
+
+    // ... and on "beta", set up the function to add a hook to alpha (but only
+    // for library 1).
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("beta", add_callout78_alpha);
+
+    // See what we get for calling the callouts on alpha first.
+    CalloutHandle callout_handle_1(getCalloutManager());
+    callout_handle_1.setArgument("handle_num", static_cast<int>(1)); 
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
+
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
+    EXPECT_EQ("111", resultCalloutString(0));
+    EXPECT_EQ("211", resultCalloutString(1));
+    EXPECT_EQ("311", resultCalloutString(2));
+
+    // All as expected, now call the callouts on beta.  This should add a
+    // callout to the list of callouts for alpha, which we should see when
+    // we run the test again.
+    getCalloutManager()->callCallouts(beta_index_, callout_handle_1);
+
+    // Use a new callout handle so as to get fresh callout context.
+    CalloutHandle callout_handle_2(getCalloutManager());
+    callout_handle_2.setArgument("handle_num", static_cast<int>(2)); 
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
+
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
+    EXPECT_EQ("112", resultCalloutString(0));
+    EXPECT_EQ("212782", resultCalloutString(1));
+    EXPECT_EQ("312", resultCalloutString(2));
+}
+
+// Check that a callout can register another callout on the same hook.
+// Note that the registration only applies to a subsequent invocation of
+// callCallouts, not to the current one. In other words, if
+//
+// * the callout list for a library is "A then B then C"
+// * when callCallouts is executed "B" adds "D" to that list,
+//
+// ... the current execution of callCallouts only executes A, B and C.  A
+// subsequent invocation will execute A, B, C then D.
+
+TEST_F(HandlesTest, DynamicRegistrationSameHook) {
+    // Register callouts for the different libraries.
+    CalloutHandle handle(getCalloutManager());
+
+    // Set up callouts on "alpha".
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout11);
+    getCalloutManager()->registerCallout("alpha", add_callout78_alpha);
+    getCalloutManager()->registerCallout("delta", print1);
+
+    // See what we get for calling the callouts on alpha first.
+    CalloutHandle callout_handle_1(getCalloutManager());
+    callout_handle_1.setArgument("handle_num", static_cast<int>(1)); 
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
+    EXPECT_EQ("111", resultCalloutString(0));
+
+    // Run it again - we should have added something to this hook.
+    CalloutHandle callout_handle_2(getCalloutManager());
+    callout_handle_2.setArgument("handle_num", static_cast<int>(2)); 
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
+    EXPECT_EQ("112782", resultCalloutString(0));
+
+    // And a third time...
+    CalloutHandle callout_handle_3(getCalloutManager());
+    callout_handle_3.setArgument("handle_num", static_cast<int>(3)); 
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_3);
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_3);
+    EXPECT_EQ("113783783", resultCalloutString(0));
+}
+
+// Deregistration of a callout from a different hook
+
+TEST_F(HandlesTest, DynamicDeregistrationDifferentHook) {
+    // Register callouts for the different libraries.
+    CalloutHandle handle(getCalloutManager());
+
+    // Set up callouts on "alpha".
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout11);
+    getCalloutManager()->registerCallout("alpha", callout78);
+    getCalloutManager()->registerCallout("alpha", callout11);
+    getCalloutManager()->registerCallout("delta", print1);
+
+    getCalloutManager()->registerCallout("beta", delete_callout78_alpha);
+
+    // Call the callouts on alpha
+    CalloutHandle callout_handle_1(getCalloutManager());
+    callout_handle_1.setArgument("handle_num", static_cast<int>(1)); 
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
+
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
+    EXPECT_EQ("111781111", resultCalloutString(0));
+
+    // Run the callouts on hook beta to remove the callout on alpha.
+    getCalloutManager()->callCallouts(beta_index_, callout_handle_1);
+
+    // The run of the callouts should have altered the callout list on the
+    // first library for hook alpha, so call again to make sure.
+    CalloutHandle callout_handle_2(getCalloutManager());
+    callout_handle_2.setArgument("handle_num", static_cast<int>(2)); 
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
+
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
+    EXPECT_EQ("112112", resultCalloutString(0));
+}
+
+// Deregistration of a callout from the same hook
+
+TEST_F(HandlesTest, DynamicDeregistrationSameHook) {
+    // Register callouts for the different libraries.
+    CalloutHandle handle(getCalloutManager());
+
+    // Set up callouts on "alpha".
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", callout11);
+    getCalloutManager()->registerCallout("alpha", delete_callout78_alpha);
+    getCalloutManager()->registerCallout("alpha", callout78);
+    getCalloutManager()->registerCallout("delta", print1);
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", callout21);
+    getCalloutManager()->registerCallout("alpha", callout78);
+    getCalloutManager()->registerCallout("delta", print2);
+
+    // Call the callouts on alpha
+    CalloutHandle callout_handle_1(getCalloutManager());
+    callout_handle_1.setArgument("handle_num", static_cast<int>(1)); 
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
+
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
+    EXPECT_EQ("111781", resultCalloutString(0));
+    EXPECT_EQ("211781", resultCalloutString(1));
+
+    // The run of the callouts should have altered the callout list on the
+    // first library for hook alpha, so call again to make sure.
+    CalloutHandle callout_handle_2(getCalloutManager());
+    callout_handle_2.setArgument("handle_num", static_cast<int>(2)); 
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
+
+    zero_results();
+    getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
+    EXPECT_EQ("112", resultCalloutString(0));
+    EXPECT_EQ("212782", resultCalloutString(1));
+}
+
+// Testing the operation of the "skip" flag.  Callouts print the value
+// they see in the flag and either leave it unchanged, set it or clear it.
+
+int
+calloutPrintSkip(CalloutHandle& handle) {
+    static const std::string YES("Y");
+    static const std::string NO("N");
+
+    HandlesTest::common_string_ = HandlesTest::common_string_ +
+        (handle.getSkip() ? YES : NO);
+    return (0);
+}
+
+int
+calloutSetSkip(CalloutHandle& handle) {
+    static_cast<void>(calloutPrintSkip(handle));
+    handle.setSkip(true);
+    return (0);
+}
+
+int
+calloutClearSkip(CalloutHandle& handle) {
+    static_cast<void>(calloutPrintSkip(handle));
+    handle.setSkip(false);
+    return (0);
+}
+
+// Do a series of tests, returning with the skip flag set "true".
+
+TEST_F(HandlesTest, ReturnSkipSet) {
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+
+    CalloutHandle callout_handle(getCalloutManager());
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle);
+
+    // Check result.  For each of visual checking, the expected string is
+    // divided into sections corresponding to the blocks of callouts above.
+    EXPECT_EQ(std::string("NNYY" "NNYYN" "NNYN"), common_string_);
+
+    // ... and check that the skip flag on exit from callCallouts is set.
+    EXPECT_TRUE(callout_handle.getSkip());
+}
+
+// Repeat the test, returning with the skip flag clear.
+TEST_F(HandlesTest, ReturnSkipClear) {
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+    getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
+    getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+    getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+
+    CalloutHandle callout_handle(getCalloutManager());
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle);
+
+    // Check result.  For each of visual checking, the expected string is
+    // divided into sections corresponding to the blocks of callouts above.
+    EXPECT_EQ(std::string("NYY" "NNYNYN" "NNNY"), common_string_);
+
+    // ... and check that the skip flag on exit from callCallouts is set.
+    EXPECT_FALSE(callout_handle.getSkip());
+}
+
+// The next set of callouts do a similar thing to the above "skip" tests,
+// but alter the value of a string argument.  This is for testing that the
+// a callout is able to change an argument and return it to the caller.
+
+const char* MODIFIED_ARG = "modified_arg";
+
+int
+calloutSetArgumentCommon(CalloutHandle& handle, const char* what) {
+    std::string modified_arg = "";
+
+    handle.getArgument(MODIFIED_ARG, modified_arg);
+    modified_arg = modified_arg + std::string(what);
+    handle.setArgument(MODIFIED_ARG, modified_arg);
+    return (0);
+}
+
+int
+calloutSetArgumentYes(CalloutHandle& handle) {
+    return (calloutSetArgumentCommon(handle, "Y"));
+}
+
+int
+calloutSetArgumentNo(CalloutHandle& handle) {
+    return (calloutSetArgumentCommon(handle, "N"));
+}
+
+// ... and a callout to just copy the argument to the "common_string_" variable
+// but otherwise not alter it.
+
+int
+calloutPrintArgument(CalloutHandle& handle) {
+    handle.getArgument(MODIFIED_ARG, HandlesTest::common_string_);
+    return (0);
+}
+
+TEST_F(HandlesTest, CheckModifiedArgument) {
+    getCalloutManager()->setLibraryIndex(0);
+    getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
+    getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
+    getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
+
+    getCalloutManager()->setLibraryIndex(1);
+    getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
+    getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
+    getCalloutManager()->registerCallout("alpha", calloutPrintArgument);
+    getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
+    getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
+
+    getCalloutManager()->setLibraryIndex(2);
+    getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
+    getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
+    getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
+
+    // Create the argument with an initial empty string value.  Then call the
+    // sequence of callouts above.
+    CalloutHandle callout_handle(getCalloutManager());
+    std::string modified_arg = "";
+    callout_handle.setArgument(MODIFIED_ARG, modified_arg);
+    getCalloutManager()->callCallouts(alpha_index_, callout_handle);
+
+    // Check the intermediate and results.  For visual checking, the expected
+    // string is divided into sections corresponding to the blocks of callouts
+    // above.
+    EXPECT_EQ(std::string("YNN" "YY"), common_string_);
+
+    callout_handle.getArgument(MODIFIED_ARG, modified_arg);
+    EXPECT_EQ(std::string("YNN" "YYNN" "YNY"), modified_arg);
+}
+
+
+} // Anonymous namespace
+

+ 242 - 0
src/lib/util/tests/server_hooks_unittest.cc

@@ -0,0 +1,242 @@
+// 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/server_hooks.h>
+
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+using namespace isc;
+using namespace isc::util;
+using namespace std;
+
+namespace {
+
+// Checks the registration of hooks and the interrogation methods.  As the
+// constructor registers two hooks, this is also a test of the constructor.
+
+TEST(ServerHooksTest, RegisterHooks) {
+    ServerHooks hooks;
+
+    // There should be two hooks already registered, with indexes 0 and 1.
+    EXPECT_EQ(2, hooks.getCount());
+    EXPECT_EQ(0, hooks.getIndex("context_create"));
+    EXPECT_EQ(1, hooks.getIndex("context_destroy"));
+
+    // Check that the constants are as expected. (The intermediate variables
+    // are used because of problems with g++ 4.6.1/Ubuntu 11.10 when resolving
+    // the value of the ServerHooks constants when they appeared within the
+    // gtest macro.)
+    const int create_value = ServerHooks::CONTEXT_CREATE;
+    const int destroy_value = ServerHooks::CONTEXT_DESTROY;
+    EXPECT_EQ(0, create_value);
+    EXPECT_EQ(1, destroy_value);
+
+    // Register another couple of hooks.  The test on returned index is based
+    // on knowledge that the hook indexes are assigned in ascending order.
+    int alpha = hooks.registerHook("alpha");
+    EXPECT_EQ(2, alpha);
+    EXPECT_EQ(2, hooks.getIndex("alpha"));
+
+    int beta = hooks.registerHook("beta");
+    EXPECT_EQ(3, beta);
+    EXPECT_EQ(3, hooks.getIndex("beta"));
+
+    // Should be four hooks now
+    EXPECT_EQ(4, hooks.getCount());
+}
+
+// Check that duplicate names cannot be registered.
+
+TEST(ServerHooksTest, DuplicateHooks) {
+    ServerHooks hooks;
+
+    // Ensure we can't duplicate one of the existing names.
+    EXPECT_THROW(hooks.registerHook("context_create"), DuplicateHook);
+
+    // Check we can't duplicate a newly registered hook.
+    int gamma = hooks.registerHook("gamma");
+    EXPECT_EQ(2, gamma);
+    EXPECT_THROW(hooks.registerHook("gamma"), DuplicateHook);
+}
+
+// Checks that we can get the name of the hooks.
+
+TEST(ServerHooksTest, GetHookNames) {
+    vector<string> expected_names;
+    ServerHooks hooks;
+
+    // Add names into the hooks object and to the set of expected names.
+    expected_names.push_back("alpha");
+    expected_names.push_back("beta");
+    expected_names.push_back("gamma");
+    expected_names.push_back("delta");
+    for (int i = 0; i < expected_names.size(); ++i) {
+        hooks.registerHook(expected_names[i].c_str());
+    };
+
+    // Update the expected names to include the pre-defined hook names.
+    expected_names.push_back("context_create");
+    expected_names.push_back("context_destroy");
+
+    // Get the actual hook names
+    vector<string> actual_names = hooks.getHookNames();
+
+    // For comparison, sort the names into alphabetical order and do a straight
+    // vector comparison.
+    sort(expected_names.begin(), expected_names.end());
+    sort(actual_names.begin(), actual_names.end());
+
+    EXPECT_TRUE(expected_names == actual_names);
+}
+
+// Check that getting an unknown name throws an exception.
+
+TEST(ServerHooksTest, UnknownHookName) {
+    ServerHooks hooks;
+
+    EXPECT_THROW(static_cast<void>(hooks.getIndex("unknown")), NoSuchHook);
+}
+
+// Check that the count of hooks is correct.
+
+TEST(ServerHooksTest, HookCount) {
+    ServerHooks hooks;
+
+    // Insert the names into the hooks object
+    hooks.registerHook("alpha");
+    hooks.registerHook("beta");
+    hooks.registerHook("gamma");
+    hooks.registerHook("delta");
+
+    // Should be two more hooks that the number we have registered.
+    EXPECT_EQ(6, hooks.getCount());
+}
+
+// HookRegistrationFunction tests
+
+// Declare some hook registration functions.
+
+int alpha = 0;
+int beta = 0;
+int gamma = 0;
+int delta = 0;
+
+void registerAlphaBeta(ServerHooks& hooks) {
+    alpha = hooks.registerHook("alpha");
+    beta = hooks.registerHook("beta");
+}
+
+void registerGammaDelta(ServerHooks& hooks) {
+    gamma = hooks.registerHook("gamma");
+    delta = hooks.registerHook("delta");
+}
+
+// Add them to the registration vector.  This addition should happen before
+// any tests are run, so we should start off with two functions in the
+// registration vector.
+
+HookRegistrationFunction f1(registerAlphaBeta);
+HookRegistrationFunction f2(registerGammaDelta);
+
+// This is not registered statically: it is used in the latter part of the
+// test.
+
+int epsilon = 0;
+void registerEpsilon(ServerHooks& hooks) {
+    epsilon = hooks.registerHook("epsilon");
+}
+
+// Test that the registration functions were defined and can be executed.
+
+TEST(HookRegistrationFunction, Registration) {
+
+    // The first part of the tests checks the static registration.  As there
+    // is only one list of registration functions, we have to do this first
+    // as the static registration is done outside our control, before the
+    // tests are loaded.
+
+    // Ensure that the hook numbers are initialized.
+    EXPECT_EQ(0, alpha);
+    EXPECT_EQ(0, beta);
+    EXPECT_EQ(0, gamma);
+    EXPECT_EQ(0, delta);
+
+    // Should have two hook registration functions registered.
+    EXPECT_EQ(2, HookRegistrationFunction::getFunctionVector().size());
+
+    // Execute the functions and check that four new hooks were defined (two
+    // from each function).
+    ServerHooks hooks;
+    EXPECT_EQ(2, hooks.getCount());
+    HookRegistrationFunction::execute(hooks);
+    EXPECT_EQ(6, hooks.getCount());
+
+    // Check the hook names are as expected.
+    vector<string> names = hooks.getHookNames();
+    ASSERT_EQ(6, names.size());
+    sort(names.begin(), names.end());
+    EXPECT_EQ(string("alpha"), names[0]);
+    EXPECT_EQ(string("beta"), names[1]);
+    EXPECT_EQ(string("context_create"), names[2]);
+    EXPECT_EQ(string("context_destroy"), names[3]);
+    EXPECT_EQ(string("delta"), names[4]);
+    EXPECT_EQ(string("gamma"), names[5]);
+
+    // Check that numbers in the range 2-5 inclusive were assigned as the
+    // hook indexes (0 and 1 being reserved for context_create and
+    // context_destroy).
+    vector<int> indexes;
+    indexes.push_back(alpha);
+    indexes.push_back(beta);
+    indexes.push_back(gamma);
+    indexes.push_back(delta);
+    sort(indexes.begin(), indexes.end());
+    EXPECT_EQ(2, indexes[0]);
+    EXPECT_EQ(3, indexes[1]);
+    EXPECT_EQ(4, indexes[2]);
+    EXPECT_EQ(5, indexes[3]);
+
+    // One last check.  We'll test that the constructor of does indeed
+    // add a function to the function vector and that the static initialization
+    // was not somehow by chance.
+    HookRegistrationFunction::getFunctionVector().clear();
+    EXPECT_TRUE(HookRegistrationFunction::getFunctionVector().empty());
+    epsilon = 0;
+
+    // Register a single registration function.
+    HookRegistrationFunction f3(registerEpsilon);
+    EXPECT_EQ(1, HookRegistrationFunction::getFunctionVector().size());
+
+    // Execute it...
+    ServerHooks hooks2;
+    EXPECT_EQ(0, epsilon);
+    EXPECT_EQ(2, hooks2.getCount());
+    HookRegistrationFunction::execute(hooks2);
+
+    // There should be three hooks, with the new one assigned an index of 2.
+    names = hooks2.getHookNames();
+    ASSERT_EQ(3, names.size());
+    sort(names.begin(), names.end());
+    EXPECT_EQ(string("context_create"), names[0]);
+    EXPECT_EQ(string("context_destroy"), names[1]);
+    EXPECT_EQ(string("epsilon"), names[2]);
+
+    EXPECT_EQ(2, epsilon);
+}
+
+} // Anonymous namespace