Browse Source

[2974] LibraryHandle context methods added

Use of boost::any allows different types to be stored in one
collection.
Stephen Morris 12 years ago
parent
commit
6ac0a09141

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

@@ -37,6 +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/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

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

@@ -0,0 +1,127 @@
+// 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

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

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

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

@@ -0,0 +1,203 @@
+// 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