Browse Source

[2974] Added the LibraryHandle class to the hooks framework

Stephen Morris 12 years ago
parent
commit
bf1e13564a

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

@@ -37,7 +37,7 @@ 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_handles.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

+ 0 - 127
src/lib/util/hooks/callout_handles.h

@@ -1,127 +0,0 @@
-// 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 <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 {
-class CalloutHandle;    // Forward declaration for CalloutHandle
-
-/// Typedef for a callout pointer
-extern "C" {
-    typedef int (*CalloutPtr)(const CalloutHandle&);
-};
-
-
-/// @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) {}
-};
-/// @brief Global Callout Handle
-/// @todo Change name to 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 an 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), 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);
-    }
-
-private:
-    /// 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
-
-    /// Index of this library in the list of libraries
-    int index_;
-};
-
-} // namespace util
-} // namespace isc
-
-#endif // CALLOUT_HANDLE_H

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

@@ -0,0 +1,138 @@
+// 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/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()) && (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

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

@@ -0,0 +1,236 @@
+// 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 Global Callout Handle
+/// @todo Change name to 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 an 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 indesx 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

+ 1 - 2
src/lib/util/hooks/server_hooks.h

@@ -24,11 +24,10 @@
 namespace isc {
 namespace util {
 
-/// @brief Duplicate Hook
+/// @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) :

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

@@ -25,12 +25,12 @@ 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_handles_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

+ 0 - 203
src/lib/util/tests/callout_handles_unittest.cc

@@ -1,203 +0,0 @@
-// 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_handles.h>
-#include <util/hooks/server_hooks.h>
-
-#include <gtest/gtest.h>
-
-using namespace isc::util;
-using namespace std;
-
-namespace {
-
-class CalloutHandlesTest : public ::testing::Test {
-public:
-    /// @brief Constructor
-    ///
-    /// Sets up an appropriate number of server hooks to pass to the
-    /// constructed callout handle objects.
-    CalloutHandlesTest() : hooks_(new ServerHooks()) {
-        hooks_->registerHook("alpha");
-        hooks_->registerHook("beta");
-        hooks_->registerHook("gamma");
-    }
-
-    /// Obtain constructed server hooks
-    boost::shared_ptr<ServerHooks> getServerHooks() {
-        return (hooks_);
-    }
-
-private:
-    boost::shared_ptr<ServerHooks> hooks_;
-};
-
-// Test that we can store multiple values of the same type and that they
-// are distinct.
-
-TEST_F(CalloutHandlesTest, 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(CalloutHandlesTest, 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(CalloutHandlesTest, 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(CalloutHandlesTest, 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(CalloutHandlesTest, 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 = &aleph;
-    const Beta* pcb = &beth;
-
-    // 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);
-}
-
-} // Anonymous namespace

+ 448 - 0
src/lib/util/tests/library_handle_unittest.cc

@@ -0,0 +1,448 @@
+// 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/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 CalloutHandle {};
+}
+}
+
+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()) {
+        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_);
+    }
+
+    /// 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_;
+};
+
+// 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 = &aleph;
+    const Beta* pcb = &beth;
+
+    // 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);
+}
+
+};  // 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 dummy;
+    int status = handle.callCallouts(index, dummy);
+
+    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 dummy;
+    int status = handle.callCallouts(index, dummy);
+
+    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 dummy;
+    int status = handle.callCallouts(index, dummy);
+
+    EXPECT_EQ(1, 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 dummy;
+    int status = handle.callCallouts(index, dummy);
+
+    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, Degreister) {
+    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 dummy;
+    int status = handle.callCallouts(index, dummy);
+
+    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")));
+}
+
+
+} // Anonymous namespace