BIND 10 trac2974, updated. 26f53e4d06284cf0854ec06d14b5071b26996347 [2974] Added first part of CalloutHandle code

BIND 10 source code commits bind10-changes at lists.isc.org
Mon May 27 18:46:43 UTC 2013


The branch, trac2974 has been updated
       via  26f53e4d06284cf0854ec06d14b5071b26996347 (commit)
       via  d219feca6015a99293d37d030ad791263432f533 (commit)
       via  bf1e13564ab29539beb465d212a51e895e640915 (commit)
       via  63e17482bdd2e68e56f2642267169a101af9e012 (commit)
       via  6ac0a0914155c3379e6608d3b6bc884ba1e59527 (commit)
       via  82679565adb6001d2b445cc932935d9a0561fafc (commit)
      from  42073d9d8c264a33ca883c9e80310cb91de706c6 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 26f53e4d06284cf0854ec06d14b5071b26996347
Author: Stephen Morris <stephen at isc.org>
Date:   Mon May 27 19:45:24 2013 +0100

    [2974] Added first part of CalloutHandle code
    
    This part sets and gets the argument list and the skip flag.
    Also modified the LibraryCallback code to handle the skip flag, and
    added another using test to check that.

commit d219feca6015a99293d37d030ad791263432f533
Author: Stephen Morris <stephen at isc.org>
Date:   Mon May 27 18:52:58 2013 +0100

    [2974] Added missing test to LibraryHandle unit tests

commit bf1e13564ab29539beb465d212a51e895e640915
Author: Stephen Morris <stephen at isc.org>
Date:   Mon May 27 18:16:05 2013 +0100

    [2974] Added the LibraryHandle class to the hooks framework

commit 63e17482bdd2e68e56f2642267169a101af9e012
Author: Stephen Morris <stephen at isc.org>
Date:   Mon May 27 14:46:39 2013 +0100

    [2974] Added missing ServerHooks test
    
    Added test for the getCount() method, and tidied up the documentation
    for the ServerHooks class.

commit 6ac0a0914155c3379e6608d3b6bc884ba1e59527
Author: Stephen Morris <stephen at isc.org>
Date:   Mon May 27 14:32:10 2013 +0100

    [2974] LibraryHandle context methods added
    
    Use of boost::any allows different types to be stored in one
    collection.

commit 82679565adb6001d2b445cc932935d9a0561fafc
Author: Stephen Morris <stephen at isc.org>
Date:   Mon May 27 12:05:53 2013 +0100

    [2974] Added ServerHooks class
    
    This is the first part of the hooks implementation, a class that
    carries the list of hooks.

-----------------------------------------------------------------------

Summary of changes:
 src/lib/util/Makefile.am                      |    3 +
 src/lib/util/hooks/callout_handle.h           |  162 ++++++++
 src/lib/util/hooks/library_handle.cc          |  141 +++++++
 src/lib/util/hooks/library_handle.h           |  235 ++++++++++++
 src/lib/util/hooks/server_hooks.cc            |   94 +++++
 src/lib/util/hooks/server_hooks.h             |  115 ++++++
 src/lib/util/tests/Makefile.am                |    3 +
 src/lib/util/tests/callout_handle_unittest.cc |  305 +++++++++++++++
 src/lib/util/tests/library_handle_unittest.cc |  509 +++++++++++++++++++++++++
 src/lib/util/tests/server_hooks_unittest.cc   |  112 ++++++
 10 files changed, 1679 insertions(+)
 create mode 100644 src/lib/util/hooks/callout_handle.h
 create mode 100644 src/lib/util/hooks/library_handle.cc
 create mode 100644 src/lib/util/hooks/library_handle.h
 create mode 100644 src/lib/util/hooks/server_hooks.cc
 create mode 100644 src/lib/util/hooks/server_hooks.h
 create mode 100644 src/lib/util/tests/callout_handle_unittest.cc
 create mode 100644 src/lib/util/tests/library_handle_unittest.cc
 create mode 100644 src/lib/util/tests/server_hooks_unittest.cc

-----------------------------------------------------------------------
diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am
index ff5ef40..8e42eff 100644
--- a/src/lib/util/Makefile.am
+++ b/src/lib/util/Makefile.am
@@ -37,6 +37,9 @@ 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_handle.h
+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
 
diff --git a/src/lib/util/hooks/callout_handle.h b/src/lib/util/hooks/callout_handle.h
new file mode 100644
index 0000000..7426957
--- /dev/null
+++ b/src/lib/util/hooks/callout_handle.h
@@ -0,0 +1,162 @@
+// 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 <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 to use an invalid argument name.
+class NoSuchArgument : public Exception {
+public:
+    NoSuchArgument(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+// Forward declaration of the hook manager class
+class HookManager;
+
+/// @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.
+
+class CalloutHandle {
+private:
+    /// Typedef to allow abbreviation of iterator specification in methods
+    typedef std::map<std::string, boost::any> ArgumentCollection;
+
+public:
+
+    /// @brief Constructor
+    ///
+    /// @param manager Pointer to the HookManager object controlling the
+    ///        the operations of the hooks.
+    CalloutHandle(boost::shared_ptr<HookManager>& manager)
+        : arguments_(), manager_(manager), skip_(false)
+    {}
+
+    /// @brief Set argument
+    ///
+    /// Sets an argument.  If the argument 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 setArgument(const std::string& name, T value) {
+        arguments_[name] = value;
+    }
+
+    /// @brief Get argument
+    ///
+    /// Gets an argument.  If an argument of the given name does not exist,
+    /// a "NoSuchArgument" exception is thrown.
+    ///
+    /// @param name Name of the element in the argument list to set.
+    /// @param value [out] Value to set.  The type of "value" is important:
+    ///        it must match the type of the value set.
+    ///
+    /// @throw NoSuchArgument Thrown if no argument with the name "name" is
+    ///        present.
+    /// @throw boost::bad_any_cast Thrown if the context element is present,
+    ///        but the type of the element is not that expected
+    template <typename T>
+    void getArgument(const std::string& name, T& value) const {
+        ArgumentCollection::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 {
+        std::vector<std::string> a;
+        return (a);
+    }
+
+    /// @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.
+    ///
+    /// @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.
+    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 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_);
+    }
+
+
+private:
+    /// Collection of arguments passed to the callouts
+    ArgumentCollection arguments_;
+
+    /// Controlling hook manager
+    boost::shared_ptr<HookManager> manager_;   ///< Controlling hook manager
+
+    /// "Skip" flag, indicating if the caller should bypass remaining callouts.
+    bool skip_;
+};
+
+} // namespace util
+} // namespace isc
+
+
+#endif // CALLOUT_HANDLE_H
diff --git a/src/lib/util/hooks/library_handle.cc b/src/lib/util/hooks/library_handle.cc
new file mode 100644
index 0000000..bf05037
--- /dev/null
+++ b/src/lib/util/hooks/library_handle.cc
@@ -0,0 +1,141 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/hooks/callout_handle.h>
+#include <util/hooks/library_handle.h>
+
+#include <algorithm>
+#include <functional>
+
+using namespace std;
+using namespace isc::util;
+
+namespace isc {
+namespace util {
+
+// Check that an index is valid for the hook vector
+void
+LibraryHandle::checkHookIndex(int index) const {
+    if ((index < 0) || (index >= hook_vector_.size())) {
+        isc_throw(NoSuchHook, "unable to call callout for hook index " <<
+                  index << ": index is invalid for the size of the hook "
+                  "vector (" << hook_vector_.size() << ")");
+    }
+}
+
+// Get index for named hook
+int
+LibraryHandle::getHookIndex(const std::string& name) const {
+
+    // Get index of hook in the hook vector.
+    int index = hooks_->getIndex(name);
+    if (index < 0) {
+        isc_throw(NoSuchHook, "unknown hook: " << name);
+    } else if (index >= hook_vector_.size()) {
+        isc_throw(Unexpected, "hook name " << name << " is valid, but the "
+                  "index returned (" << index << ") is invalid for the size of "
+                  "the LibraryHandle::hook_vector_ (" << hook_vector_.size() <<
+                  ")");
+    }
+
+    return (index);
+}
+
+// Register a callout at the back of the named hook
+
+void
+LibraryHandle::registerCallout(const std::string& name, CalloutPtr callout) {
+
+    // Get index of hook in the hook vector, validating the hook name as we
+    // do so.
+    int index = getHookIndex(name);
+
+    // Index valid, so add the callout to the end of the list.
+    hook_vector_[index].push_back(callout);
+}
+
+// Check if callouts are present for a given hook index.
+
+bool
+LibraryHandle::calloutsPresent(int index) const {
+
+    // Validate the hook index.
+    checkHookIndex(index);
+
+    // Valid, so are there any callouts associated with that hook?
+    return (!hook_vector_[index].empty());
+}
+
+// Call all the callouts for a given hook.
+
+int
+LibraryHandle::callCallouts(int index, CalloutHandle& handle) {
+
+    // Validate the hook index.
+    checkHookIndex(index);
+
+    // Call all the callouts, stopping if a non-zero status is returned.
+    // @todo also need to stop if the callout handle "skip" flag is set.
+    int status = 0;
+    for (int i = 0;
+         (i < hook_vector_[index].size()) && !handle.getSkip() && (status == 0);
+          ++i) {
+        status = (*hook_vector_[index][i])(handle);
+    }
+
+    return (status);
+}
+
+// Deregister a callout
+
+void
+LibraryHandle::deregisterCallout(const std::string& name, CalloutPtr callout) {
+
+    // Get the index associated with this hook (validating the name in the
+    // process).
+    int index = getHookIndex(name);
+
+    if (!hook_vector_[index].empty()) {
+        // The next bit is standard STL (see "Item 33" in "Effective STL" by
+        // Scott Meyters.
+        //
+        // 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_[index].erase(remove_if(hook_vector_[index].begin(),
+                                            hook_vector_[index].end(),
+                                            bind1st(equal_to<CalloutPtr>(),
+                                                    callout)),
+                                  hook_vector_[index].end());
+    }
+}
+
+// Deregister all callouts
+
+void
+LibraryHandle::deregisterAll(const std::string& name) {
+
+    // Get the index associated with this hook (validating the name in the
+    // process).
+    int index = getHookIndex(name);
+
+    // Get rid of everything.
+    hook_vector_[index].clear();
+}
+
+} // namespace util
+} // namespace isc
diff --git a/src/lib/util/hooks/library_handle.h b/src/lib/util/hooks/library_handle.h
new file mode 100644
index 0000000..f5fa81a
--- /dev/null
+++ b/src/lib/util/hooks/library_handle.h
@@ -0,0 +1,235 @@
+// 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 <map>
+#include <string>
+
+#include <boost/any.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <exceptions/exceptions.h>
+#include <util/hooks/server_hooks.h>
+
+namespace isc {
+namespace util {
+
+/// @brief No such hook
+///
+/// Thrown if an attempt is made to use an invalid hook name or hook index.
+class NoSuchHook : public Exception {
+public:
+    NoSuchHook(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+/// @brief No Such Context
+///
+/// Thrown if an attempt is made to obtain context that has not been previously
+/// set.
+
+class NoSuchContext : public Exception {
+public:
+    NoSuchContext(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+// Forward declaration for CalloutHandle
+class CalloutHandle;
+
+/// Typedef for a callout pointer
+extern "C" {
+    typedef int (*CalloutPtr)(CalloutHandle&);
+};
+
+
+/// @brief Library handle
+///
+/// This class is used to manage a loaded library.  It is used by the user
+/// library to register callouts and by the HookManager to call them.  The
+/// class also contains storage for library-specific context.
+///
+/// Although there is a persuasive argument for the class to load unload the
+/// user library, that is handled by the HookManager to prevent the user library
+/// from accessing those functions.
+
+class LibraryHandle {
+private:
+    /// Typedef to allow abbreviation of iterator specification in methods
+    typedef std::map<std::string, boost::any> ContextCollection;
+
+public:
+
+    /// @brief Constructor
+    ///
+    /// This is passed the ServerHooks object and an index number: the former
+    /// allows for the sizing of the internal hook vector, and the latter
+    /// is used by the CalloutHandle object to access appropriate context
+    ///
+    /// @param hooks Pointer to the hooks registered by the server.
+    /// @param index Index of this library in the list of loaded libraries.
+    LibraryHandle(boost::shared_ptr<ServerHooks>& hooks, int index)
+        : context_(), hooks_(hooks), hook_vector_(hooks->getCount()),
+          index_(index)
+    {}
+
+    /// @brief Set context
+    ///
+    /// Sets an element in the library context.  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) {
+        context_[name] = value;
+    }
+
+    /// @brief Get context
+    ///
+    /// Sets an element in the library context.  If the name does not exist,
+    /// a "NoSuchContext" exception is thrown.
+    ///
+    /// @param name Name of the element in the context to set.
+    /// @param value [out] Value to set.  The type of "value" is important:
+    ///        it must match the type of the value set.
+    ///
+    /// @throw NoSuchContext Thrown if no context element with the name
+    ///        "name" is present.
+    /// @throw boost::bad_any_cast Thrown if the context element is present,
+    ///        but the type of the element is not that expected
+    template <typename T>
+    void getContext(const std::string& name, T& value) const {
+        ContextCollection::const_iterator element_ptr = context_.find(name);
+        if (element_ptr == context_.end()) {
+            isc_throw(NoSuchContext, "unable to find library context datum " <<
+                      name << " in library at index " << index_);
+        }
+
+        value = boost::any_cast<T>(element_ptr->second);
+    }
+
+    /// @brief Register a callout
+    ///
+    /// Registers a callout function with a given hook.  The callout is added
+    /// to the end of the callouts associated with the 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 Thrown if the hook name is unrecognised.
+    /// @throw Unexpected Hooks name is valid but internal data structure is
+    ///        of the wrong size.
+    void registerCallout(const std::string& name, CalloutPtr callout);
+
+    /// @brief De-Register a callout
+    ///
+    /// Searches through the functions associated with the named hook and
+    /// removes all entries matching the callout.  If there are no matching
+    /// callouts, the result is a no-op.
+    ///
+    /// @param name Name of the hook from which the callout is removed.
+    /// @param callout Pointer to the callout function to be removed.
+    ///
+    /// @throw NoSuchHook Thrown if the hook name is unrecognised.
+    /// @throw Unexpected Hooks name is valid but internal data structure is
+    ///        of the wrong size.
+    void deregisterCallout(const std::string& name, CalloutPtr callout);
+
+    /// @brief Removes all callouts
+    ///
+    /// Removes all callouts associated with a given hook.  This is a no-op
+    /// if there are no callouts associated with the hook.
+    ///
+    /// @param name Name of the hook from which the callouts are removed.
+    ///
+    /// @throw NoSuchHook Thrown if the hook name is unrecognised.
+    void deregisterAll(const std::string& name);
+
+
+    /// @brief Checks if callouts are present
+    ///
+    /// @param index Hook index for which callouts are checked.
+    ///
+    /// @return true if callouts are present, false if not.
+    ///
+    /// @throw NoSuchHook Thrown if the index is not valid.
+    bool calloutsPresent(int index) const;
+
+    /// @brief Calls the callouts for a given hook
+    ///
+    /// Calls the callouts associated with the given hook index.
+    ///
+    /// @param index Index of the hook to call.
+    /// @param handle Reference to the CalloutHandle object for the current
+    ///        object being processed.
+    ///
+    /// @return Status return.
+    ///
+    int callCallouts(int index, CalloutHandle& handle);
+
+private:
+    /// @brief Check hook index
+    ///
+    /// Checks that the hook index is valid for the hook vector.  If not,
+    /// an exception is thrown.
+    ///
+    /// @param index Hooks index to check.
+    ///
+    /// @throw NoSuchHook Thrown if the index is not valid for the hook vector.
+    void checkHookIndex(int index) const;
+
+    /// @brief Get hook index
+    ///
+    /// Utility function to return the index associated with a hook name. It
+    /// also checks for validity of the index: if the name is valid, the
+    /// index should be valid.  However, as the class only keeps a pointer to
+    /// a shared ServerHooks object, it is possible that the object was modified
+    /// after the hook_vector_ was sized.  This function performs some checks
+    /// on the name and throws an exception if the checks fail.
+    ///
+    /// @param name Name of the hook to check
+    ///
+    /// @return Index of the hook in the hook_vector_
+    ///
+    /// @throw NoSuchHook Thrown if the hook name is unrecognised.
+    /// @throw Unexpected Index not valid for the hook vector.
+    int getHookIndex(const std::string& name) const;
+
+    /// @brief Callout pointers equal
+    ///
+    /// Unary predicate to 
+
+    // Member variables
+
+    /// Context - mapping of names variables that can be of different types.
+    ContextCollection context_;
+
+    /// Pointer to the list of hooks registered by the server
+    boost::shared_ptr<ServerHooks>      hooks_;     ///< Pointer to hooks
+
+    /// Each element in the following vector corresponds to a single hook and
+    /// is an ordered list of callouts for that hook.
+    std::vector<std::vector<CalloutPtr> >  hook_vector_;
+
+    /// Index of this library in the list of libraries
+    int index_;
+};
+
+} // namespace util
+} // namespace isc
+
+#endif // LIBRARY_HANDLE_H
diff --git a/src/lib/util/hooks/server_hooks.cc b/src/lib/util/hooks/server_hooks.cc
new file mode 100644
index 0000000..6d5a911
--- /dev/null
+++ b/src/lib/util/hooks/server_hooks.cc
@@ -0,0 +1,94 @@
+// 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>
+
+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.
+
+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 or return -1 if not found
+
+int
+ServerHooks::getIndex(const string& name) const {
+
+    // Return pair of <hook name, index>.
+    HookCollection::const_iterator i = hooks_.find(name);
+    if (i == hooks_.end()) {
+        return (-1);
+    }
+
+    return (i->second);
+}
+
+// Return list of hooks
+
+std::vector<std::string>
+ServerHooks::getHookNames() const {
+
+    std::vector<std::string> names;
+    HookCollection::const_iterator i;
+    for (i = hooks_.begin(); i != hooks_.end(); ++i) {
+        names.push_back(i->first);
+    }
+
+    return (names);
+}
+
+
+} // namespace util
+} // namespace isc
diff --git a/src/lib/util/hooks/server_hooks.h b/src/lib/util/hooks/server_hooks.h
new file mode 100644
index 0000000..7aa5acb
--- /dev/null
+++ b/src/lib/util/hooks/server_hooks.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 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 Server Hook List
+///
+/// This class is used by the server-side code to register hooks - points at
+/// 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.
+
+class ServerHooks {
+public:
+
+    /// 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.  These
+    /// 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 calls.  This will
+    ///         be greater than or equal to zero.
+    ///
+    /// @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. A value of
+    ///         -1 is returned if no hook of the given name is found.
+    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
+};
+
+} // namespace util
+} // namespace isc
+
+#endif  // SERVER_HOOKS_H
diff --git a/src/lib/util/tests/Makefile.am b/src/lib/util/tests/Makefile.am
index ab85fa2..618b4c1 100644
--- a/src/lib/util/tests/Makefile.am
+++ b/src/lib/util/tests/Makefile.am
@@ -25,11 +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 += 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 += io_utilities_unittest.cc
+run_unittests_SOURCES += library_handle_unittest.cc
 run_unittests_SOURCES += lru_list_unittest.cc
 run_unittests_SOURCES += memory_segment_local_unittest.cc
 if USE_SHARED_MEMORY
@@ -39,6 +41,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
diff --git a/src/lib/util/tests/callout_handle_unittest.cc b/src/lib/util/tests/callout_handle_unittest.cc
new file mode 100644
index 0000000..79e536d
--- /dev/null
+++ b/src/lib/util/tests/callout_handle_unittest.cc
@@ -0,0 +1,305 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/hooks/callout_handle.h>
+#include <util/hooks/library_handle.h>
+#include <util/hooks/server_hooks.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <gtest/gtest.h>
+
+using namespace isc::util;
+using namespace std;
+
+// Dummy class for testing
+namespace isc {
+namespace util {
+class HookManager {
+public:
+    HookManager() {}
+};
+}
+}
+
+using namespace isc::util;
+using namespace std;
+
+namespace {
+
+class CalloutHandleTest : public ::testing::Test {
+public:
+    /// @brief Constructor
+    ///
+    /// Sets up an appropriate number of server hooks to pass to the
+    /// constructed callout handle objects.
+    CalloutHandleTest() : manager_(new HookManager()) {
+    }
+
+    /// Obtain hook manager
+    boost::shared_ptr<HookManager>& getHookManager() {
+        return (manager_);
+    }
+
+private:
+    boost::shared_ptr<HookManager> 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(getHookManager());
+
+    // 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 = -1;
+    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 = -1;
+    handle.getArgument("short", f);
+    EXPECT_EQ(81, f);
+}
+
+// Test that trying to get something with an incorrect name throws an
+// exception.
+
+TEST_F(CalloutHandleTest, ArgumentUnknownName) {
+    CalloutHandle handle(getHookManager());
+
+    // 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 = -1;
+    EXPECT_THROW(handle.getArgument("unknown", c), NoSuchArgument);
+}
+
+// Test that trying to get something with an incorrect type throws an exception.
+
+TEST_F(CalloutHandleTest, ArgumentIncorrectType) {
+    CalloutHandle handle(getHookManager());
+
+    // 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(getHookManager());
+
+    // 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(getHookManager());
+
+    // Declare a couple of variables, const and non-const.
+    Alpha aleph(5, 10);
+    const Beta beth(15, 20);
+
+    Alpha* pa = ℵ
+    const Beta* pcb = ℶ
+
+    // 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);
+}
+
+// Test that we can delete and argument.
+
+TEST_F(CalloutHandleTest, DeleteArgument) {
+    CalloutHandle handle(getHookManager());
+
+    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(getHookManager());
+
+    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(getHookManager());
+
+    // 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
diff --git a/src/lib/util/tests/library_handle_unittest.cc b/src/lib/util/tests/library_handle_unittest.cc
new file mode 100644
index 0000000..1f47d1b
--- /dev/null
+++ b/src/lib/util/tests/library_handle_unittest.cc
@@ -0,0 +1,509 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/hooks/callout_handle.h>
+#include <util/hooks/library_handle.h>
+#include <util/hooks/server_hooks.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc::util;
+using namespace std;
+
+// Dummy class for testing
+namespace isc {
+namespace util {
+class HookManager {};
+}
+}
+
+namespace {
+
+class LibraryHandleTest : public ::testing::Test {
+public:
+    /// @brief Constructor
+    ///
+    /// Sets up an appropriate number of server hooks to pass to the
+    /// constructed callout handle objects.
+    LibraryHandleTest()
+        : hooks_(new ServerHooks()), manager_(new HookManager()) {
+        hooks_->registerHook("alpha");
+        hooks_->registerHook("beta");
+        hooks_->registerHook("gamma");
+
+        // Also initialize the callout variables.
+        one_count = 0;
+        two_count = 0;
+        callout_value = 0;
+    }
+
+    /// Obtain constructed server hooks
+    boost::shared_ptr<ServerHooks>& getServerHooks() {
+        return (hooks_);
+    }
+
+    // Obtain constructed hook manager
+    boost::shared_ptr<HookManager>& getHookManager() {
+        return (manager_);
+    }
+
+    /// Variables for callouts test. These are public and static to allow non-
+    /// member functions to access them, but declared as class variables to
+    /// allow initialization every time the test starts.
+    static int one_count;
+    static int two_count;
+    static int callout_value;
+
+private:
+    boost::shared_ptr<ServerHooks> hooks_;
+    boost::shared_ptr<HookManager> manager_;
+};
+
+// Definition of the static variables.
+int LibraryHandleTest::one_count = 0;
+int LibraryHandleTest::two_count = 0;
+int LibraryHandleTest::callout_value = 0;
+
+// *** Context Tests ***
+//
+// The first set of tests check that the LibraryHandle can store and retrieve
+// context.
+
+// Test that we can store multiple values of the same type and that they
+// are distinct.
+
+TEST_F(LibraryHandleTest, ContextDistinctSimpleType) {
+    LibraryHandle handle(getServerHooks(), 1);
+
+    // Store and retrieve an int (random value).
+    int a = 42;
+    handle.setContext("integer1", a);
+    EXPECT_EQ(42, a);
+
+    int b = 0;
+    handle.getContext("integer1", b);
+    EXPECT_EQ(42, b);
+
+    // Add another integer (another random value).
+    int c = 142;
+    handle.setContext("integer2", c);
+    EXPECT_EQ(142, c);
+
+    int d = -1;
+    handle.getContext("integer2", d);
+    EXPECT_EQ(142, d);
+
+    // Add a short (random value).
+    short e = 81; 
+    handle.setContext("short", e);
+    EXPECT_EQ(81, e);
+
+    short f = -1;
+    handle.getContext("short", f);
+    EXPECT_EQ(81, f);
+}
+
+// Test that trying to get something with an incorrect name throws an
+// exception.
+
+TEST_F(LibraryHandleTest, ContextUnknownName) {
+    LibraryHandle handle(getServerHooks(), 1);
+
+    // Set an integer
+    int a = 42;
+    handle.setContext("integer1", a);
+    EXPECT_EQ(42, a);
+
+    // Check we can retrieve it
+    int b = 0;
+    handle.getContext("integer1", b);
+    EXPECT_EQ(42, b);
+
+    // Check that getting an unknown name throws an exception.
+    int c = -1;
+    EXPECT_THROW(handle.getContext("unknown", c), NoSuchContext);
+}
+
+// Test that trying to get something with an incorrect type throws an exception.
+
+TEST_F(LibraryHandleTest, ContextIncorrectType) {
+    LibraryHandle handle(getServerHooks(), 1);
+
+    // Set an integer
+    int a = 42;
+    handle.setContext("integer1", a);
+    EXPECT_EQ(42, a);
+
+    // Check we can retrieve it
+    long b = 0;
+    EXPECT_THROW(handle.getContext("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(LibraryHandleTest, ComplexTypes) {
+    LibraryHandle handle(getServerHooks(), 1);
+
+    // 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.setContext("aleph", aleph);
+
+    Beta beth(11, 22);
+    EXPECT_EQ(11, beth.c);
+    EXPECT_EQ(22, beth.d);
+    handle.setContext("beth", beth);
+
+    // Ensure we can extract the data correctly
+    Alpha aleph2;
+    EXPECT_EQ(0, aleph2.a);
+    EXPECT_EQ(0, aleph2.b);
+    handle.getContext("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.getContext("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.getContext("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(LibraryHandleTest, PointerTypes) {
+    LibraryHandle handle(getServerHooks(), 1);
+
+    // Declare a couple of variables, const and non-const.
+    Alpha aleph(5, 10);
+    const Beta beth(15, 20);
+
+    Alpha* pa = ℵ
+    const Beta* pcb = ℶ
+
+    // Check pointers can be set and retrieved OK
+    handle.setContext("non_const_pointer", pa);
+    handle.setContext("const_pointer", pcb);
+
+    Alpha* pa2 = 0;
+    handle.getContext("non_const_pointer", pa2);
+    EXPECT_TRUE(pa == pa2);
+
+    const Beta* pcb2 = 0;
+    handle.getContext("const_pointer", pcb2);
+    EXPECT_TRUE(pcb == pcb2);
+
+    // Check that the "const" is protected in the context.
+    const Alpha* pca3;
+    EXPECT_THROW(handle.getContext("non_const_pointer", pca3),
+                 boost::bad_any_cast);
+
+    Beta* pb3;
+    EXPECT_THROW(handle.getContext("const_pointer", pb3),
+                 boost::bad_any_cast);
+}
+
+// *** Callout Tests ***
+//
+// The next set of tests check that callouts can be registered.
+
+// Supply callouts structured in such a way that we can determine the order
+// that they are called and whether they are called at all. In particular
+// if the callout order is:
+//
+// * one followed by two, the resulting value is 20
+// * two followed by one, the resuling value is -10
+// * one and two is not called, the resulting value is 10
+// * two and one is not called, the resulting value is -20
+// * neither called, the resulting value is 0
+//
+// The variable xxx_count is the number of times the function has been called
+// in the current test.
+
+extern "C" {
+int one(CalloutHandle&) {
+
+    ++LibraryHandleTest::one_count;
+    if (LibraryHandleTest::callout_value == 0) {
+        LibraryHandleTest::callout_value = 10;
+    } else {
+        LibraryHandleTest::callout_value = -10;
+    }
+
+    return (0);
+}
+
+int two(CalloutHandle&) {
+
+    ++LibraryHandleTest::two_count;
+    if (LibraryHandleTest::callout_value == 10) {
+        LibraryHandleTest::callout_value = 20;
+    } else {
+        LibraryHandleTest::callout_value = -20;
+    }
+
+    return (0);
+}
+
+// The next function is a duplicate of "one", but returns an error status.
+
+int one_error(CalloutHandle& handle) {
+    (void) one(handle);
+    return (1);
+}
+
+// The next function is a duplicate of "one", but sets the skip flag.
+
+int one_skip(CalloutHandle& handle) {
+    (void) one(handle);
+    handle.setSkip(true);
+    return (0);
+}
+
+};  // extern "C"
+
+// Check that we can register callouts on a particular hook.
+
+TEST_F(LibraryHandleTest, RegisterSingleCallout) {
+    LibraryHandle handle(getServerHooks(), 1);
+
+    // Register callouts for hooks alpha and see that it is registered.
+    EXPECT_FALSE(handle.calloutsPresent(getServerHooks()->getIndex("alpha")));
+    handle.registerCallout("alpha", one);
+    EXPECT_TRUE(handle.calloutsPresent(getServerHooks()->getIndex("alpha")));
+
+    // Do the same for beta (which checks that the hooks are independent).
+    EXPECT_FALSE(handle.calloutsPresent(getServerHooks()->getIndex("beta")));
+    handle.registerCallout("beta", one);
+    EXPECT_TRUE(handle.calloutsPresent(getServerHooks()->getIndex("beta")));
+}
+
+// Check that we can call a single callout on a particular hook.  Refer
+// to the above definition of the callouts "one" and "two" to understand
+// the expected return values.
+
+TEST_F(LibraryHandleTest, CallSingleCallout) {
+    LibraryHandle handle(getServerHooks(), 1);
+
+    // Register callout for hook alpha...
+    EXPECT_FALSE(handle.calloutsPresent(getServerHooks()->getIndex("alpha")));
+    handle.registerCallout("alpha", one);
+    EXPECT_TRUE(handle.calloutsPresent(getServerHooks()->getIndex("alpha")));
+
+    // Call it.
+
+    EXPECT_EQ(0, LibraryHandleTest::one_count);
+    EXPECT_EQ(0, LibraryHandleTest::two_count);
+    EXPECT_EQ(0, LibraryHandleTest::callout_value);
+
+    int index = getServerHooks()->getIndex("alpha");
+    CalloutHandle callout_handle(getHookManager());
+    int status = handle.callCallouts(index, callout_handle);
+
+    EXPECT_EQ(0, status);
+    EXPECT_EQ(1, LibraryHandleTest::one_count);
+    EXPECT_EQ(0, LibraryHandleTest::two_count);
+    EXPECT_EQ(10, LibraryHandleTest::callout_value);
+
+}
+
+// Check that we can register two callouts for a hook and that they are called
+// in order.
+
+TEST_F(LibraryHandleTest, TwoCallouts) {
+    LibraryHandle handle(getServerHooks(), 1);
+
+    // Register two callouts for hook alpha...
+    handle.registerCallout("alpha", one);
+    handle.registerCallout("alpha", two);
+
+    // ... and call them.
+    EXPECT_EQ(0, LibraryHandleTest::one_count);
+    EXPECT_EQ(0, LibraryHandleTest::two_count);
+    EXPECT_EQ(0, LibraryHandleTest::callout_value);
+
+    int index = getServerHooks()->getIndex("alpha");
+    CalloutHandle callout_handle(getHookManager());
+    int status = handle.callCallouts(index, callout_handle);
+
+    EXPECT_EQ(0, status);
+    EXPECT_EQ(1, LibraryHandleTest::one_count);
+    EXPECT_EQ(1, LibraryHandleTest::two_count);
+    EXPECT_EQ(20, LibraryHandleTest::callout_value);
+}
+
+// Check that we can register two callouts for a hook and that the second is not
+// called if the first returns a non-zero status.
+
+TEST_F(LibraryHandleTest, TwoCalloutsWithError) {
+    LibraryHandle handle(getServerHooks(), 1);
+
+    // Register callout for hook alpha...
+    handle.registerCallout("alpha", one_error);
+    handle.registerCallout("alpha", two);
+
+    // Call them.
+    EXPECT_EQ(0, LibraryHandleTest::one_count);
+    EXPECT_EQ(0, LibraryHandleTest::two_count);
+    EXPECT_EQ(0, LibraryHandleTest::callout_value);
+
+    int index = getServerHooks()->getIndex("alpha");
+    CalloutHandle callout_handle(getHookManager());
+    int status = handle.callCallouts(index, callout_handle);
+
+    EXPECT_EQ(1, status);
+    EXPECT_EQ(1, LibraryHandleTest::one_count);
+    EXPECT_EQ(0, LibraryHandleTest::two_count);
+    EXPECT_EQ(10, LibraryHandleTest::callout_value);
+}
+
+// Check that we can register two callouts for a hook and that the second is not
+// called if the first returns a non-zero status.
+
+TEST_F(LibraryHandleTest, TwoCalloutsWithSkip) {
+    LibraryHandle handle(getServerHooks(), 1);
+
+    // Register callout for hook alpha...
+    handle.registerCallout("alpha", one_skip);
+    handle.registerCallout("alpha", two);
+
+    // Call them.
+    EXPECT_EQ(0, LibraryHandleTest::one_count);
+    EXPECT_EQ(0, LibraryHandleTest::two_count);
+    EXPECT_EQ(0, LibraryHandleTest::callout_value);
+
+    int index = getServerHooks()->getIndex("alpha");
+    CalloutHandle callout_handle(getHookManager());
+    int status = handle.callCallouts(index, callout_handle);
+
+    EXPECT_EQ(0, status);
+    EXPECT_EQ(1, LibraryHandleTest::one_count);
+    EXPECT_EQ(0, LibraryHandleTest::two_count);
+    EXPECT_EQ(10, LibraryHandleTest::callout_value);
+}
+
+// Check that a callout can be registered more than once.
+
+TEST_F(LibraryHandleTest, MultipleRegistration) {
+    LibraryHandle handle(getServerHooks(), 1);
+
+    // Register callouts for hook alpha...
+    handle.registerCallout("alpha", one);
+    handle.registerCallout("alpha", two);
+    handle.registerCallout("alpha", one);
+
+    // Call them.
+    EXPECT_EQ(0, LibraryHandleTest::one_count);
+    EXPECT_EQ(0, LibraryHandleTest::two_count);
+    EXPECT_EQ(0, LibraryHandleTest::callout_value);
+
+    int index = getServerHooks()->getIndex("alpha");
+    CalloutHandle callout_handle(getHookManager());
+    int status = handle.callCallouts(index, callout_handle);
+
+    EXPECT_EQ(0, status);
+    EXPECT_EQ(2, LibraryHandleTest::one_count);
+    EXPECT_EQ(1, LibraryHandleTest::two_count);
+    EXPECT_EQ(-10, LibraryHandleTest::callout_value);
+}
+
+// Check that a callout can be deregistered.
+
+TEST_F(LibraryHandleTest, Deregister) {
+    LibraryHandle handle(getServerHooks(), 1);
+
+    // Register callouts for hook alpha...
+    handle.registerCallout("alpha", one);
+    handle.registerCallout("alpha", two);
+    handle.registerCallout("alpha", one);
+
+    // Get rid of all the "one" callbacks.
+    handle.deregisterCallout("alpha", one);
+
+    // Call it.
+    EXPECT_EQ(0, LibraryHandleTest::one_count);
+    EXPECT_EQ(0, LibraryHandleTest::two_count);
+    EXPECT_EQ(0, LibraryHandleTest::callout_value);
+
+    int index = getServerHooks()->getIndex("alpha");
+    CalloutHandle callout_handle(getHookManager());
+    int status = handle.callCallouts(index, callout_handle);
+
+    EXPECT_EQ(0, status);
+    EXPECT_EQ(0, LibraryHandleTest::one_count);
+    EXPECT_EQ(1, LibraryHandleTest::two_count);
+    EXPECT_EQ(-20, LibraryHandleTest::callout_value);
+}
+
+// Check that all callouts can be deregistered.
+
+TEST_F(LibraryHandleTest, DeregisterAll) {
+    LibraryHandle handle(getServerHooks(), 1);
+
+    // Register callouts for hook alpha...
+    EXPECT_FALSE(handle.calloutsPresent(getServerHooks()->getIndex("alpha")));
+    handle.registerCallout("alpha", one);
+    handle.registerCallout("alpha", two);
+    EXPECT_TRUE(handle.calloutsPresent(getServerHooks()->getIndex("alpha")));
+
+    // ... and remove them.
+    handle.deregisterAll("alpha");
+    EXPECT_FALSE(handle.calloutsPresent(getServerHooks()->getIndex("alpha")));
+}
+
+// Add checks that invalid names etc. all throw.  With the base hooks added
+// by the constructor, there are five valid hooks, with valid indexes 0 to 4.
+
+TEST_F(LibraryHandleTest, InvalidNameAndIndex) {
+    LibraryHandle handle(getServerHooks(), 1);
+
+    EXPECT_THROW(handle.registerCallout("omega", one), NoSuchHook);
+    EXPECT_THROW(handle.deregisterCallout("omega", one), NoSuchHook);
+    EXPECT_THROW(handle.deregisterAll("omega"), NoSuchHook);
+
+    EXPECT_THROW(static_cast<void>(handle.calloutsPresent(-1)), NoSuchHook);
+    EXPECT_THROW(static_cast<void>(handle.calloutsPresent(5)), NoSuchHook);
+
+    CalloutHandle callout_handle(getHookManager());
+    EXPECT_THROW(static_cast<void>(handle.callCallouts(-1, callout_handle)),
+                 NoSuchHook);
+    EXPECT_THROW(static_cast<void>(handle.callCallouts(10, callout_handle)),
+                 NoSuchHook);
+}
+
+
+} // Anonymous namespace
diff --git a/src/lib/util/tests/server_hooks_unittest.cc b/src/lib/util/tests/server_hooks_unittest.cc
new file mode 100644
index 0000000..dd9ea1b
--- /dev/null
+++ b/src/lib/util/tests/server_hooks_unittest.cc
@@ -0,0 +1,112 @@
+// 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;
+
+// Checks the registration of hooks and the interrogation methods.  As the
+// constructor registers two hooks, this is also a test of the construction.
+
+namespace {
+
+TEST(ServerHooksTest, RegisterHooks) {
+    ServerHooks hooks;
+
+    // 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"));
+
+    // 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());
+}
+
+// Checks that duplcate names cannot be registered.
+
+TEST(ServerHooksTest, DuplicateHooks) {
+    ServerHooks hooks;
+
+    // Ensure we can duplicate one of the existing names.
+    EXPECT_THROW(hooks.registerHook("context_create"), DuplicateHook);
+
+    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;
+
+    // Insert the names into the hooks object
+    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 name sinto 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 the count of hooks is correct.
+
+TEST(ServerHooksTest, HookCount) {
+    ServerHooks hooks;
+
+    // Insert the names into the hooks object
+    hooks.registerHook("alpha");
+    hooks.registerHook("betha");
+    hooks.registerHook("gamma");
+    hooks.registerHook("delta");
+
+    // Should be two more hooks that the number we have registered.
+    EXPECT_EQ(6, hooks.getCount());
+}
+
+} // Anonymous namespace



More information about the bind10-changes mailing list