Browse Source

[master] Merge branch 2956 which adds DControllerBase and D2Controller
classes to bin/src/D2.

Thomas Markwalder 12 years ago
parent
commit
a41cac582e

+ 19 - 17
src/bin/d2/Makefile.am

@@ -18,15 +18,15 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
 
 CLEANFILES  = *.gcno *.gcda spec_config.h d2_messages.h d2_messages.cc
 
-man_MANS = b10-d2.8
+man_MANS = b10-dhcp-ddns.8
 DISTCLEANFILES = $(man_MANS)
-EXTRA_DIST = $(man_MANS) b10-d2.xml d2.spec
+EXTRA_DIST = $(man_MANS) b10-dhcp-ddns.xml dhcp-ddns.spec
 
 if GENERATE_DOCS
-b10-d2.8: b10-d2.xml
+b10-dhcp-ddns.8: b10-dhcp-ddns.xml
 	@XSLTPROC@ --novalid --xinclude --nonet -o $@ \
         http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl \
-	$(srcdir)/b10-d2.xml
+	$(srcdir)/b10-dhcp-ddns.xml
 
 else
 
@@ -44,21 +44,23 @@ d2_messages.h d2_messages.cc: d2_messages.mes
 
 BUILT_SOURCES = spec_config.h d2_messages.h d2_messages.cc
 
-pkglibexec_PROGRAMS = b10-d2
+pkglibexec_PROGRAMS = b10-dhcp-ddns
 
-b10_d2_SOURCES  = main.cc
-b10_d2_SOURCES += d2_log.cc d2_log.h
-b10_d2_SOURCES += d_process.h 
-b10_d2_SOURCES += d2_process.cc d2_process.h
+b10_dhcp_ddns_SOURCES  = main.cc
+b10_dhcp_ddns_SOURCES += d2_log.cc d2_log.h
+b10_dhcp_ddns_SOURCES += d_process.h
+b10_dhcp_ddns_SOURCES += d2_process.cc d2_process.h
+b10_dhcp_ddns_SOURCES += d_controller.cc d_controller.h
+b10_dhcp_ddns_SOURCES += d2_controller.cc d2_controller.h
 
-nodist_b10_d2_SOURCES = d2_messages.h d2_messages.cc
+nodist_b10_dhcp_ddns_SOURCES = d2_messages.h d2_messages.cc
 EXTRA_DIST += d2_messages.mes
 
-b10_d2_LDADD = $(top_builddir)/src/lib/log/libb10-log.la
-b10_d2_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
-b10_d2_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
-b10_d2_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
-b10_d2_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
+b10_dhcp_ddns_LDADD = $(top_builddir)/src/lib/log/libb10-log.la
+b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
 
-b10_d2dir = $(pkgdatadir)
-b10_d2_DATA = d2.spec
+b10_dhcp_ddnsdir = $(pkgdatadir)
+b10_dhcp_ddns_DATA = dhcp-ddns.spec

+ 13 - 9
src/bin/d2/b10-d2.xml

@@ -24,14 +24,14 @@
   </refentryinfo>
 
   <refmeta>
-    <refentrytitle>b10-d2</refentrytitle>
+    <refentrytitle>b10-dhcp-ddns</refentrytitle>
     <manvolnum>8</manvolnum>
     <refmiscinfo>BIND10</refmiscinfo>
   </refmeta>
 
   <refnamediv>
-    <refname>b10-d2</refname>
-    <refpurpose>D2 process in BIND 10 architecture</refpurpose>
+    <refname>b10-dhcp-ddns</refname>
+    <refpurpose>DHCP-DDNS process in BIND 10 architecture</refpurpose>
   </refnamediv>
 
   <docinfo>
@@ -43,14 +43,14 @@
 
   <refsynopsisdiv>
     <cmdsynopsis>
-      <command>b10-d2</command>
+      <command>b10-dhcp-ddns</command>
       <arg><option>-v</option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
   <refsynopsisdiv>
     <cmdsynopsis>
-      <command>b10-d2</command>
+      <command>b10-dhcp-ddns</command>
       <arg><option>-s</option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
@@ -59,8 +59,12 @@
   <refsect1>
     <title>DESCRIPTION</title>
     <para>
-      The <command>b10-d2</command> daemon processes requests to
-      to update DNS mapping based on DHCP lease change events.
+      The <command>b10-dhcp-ddns</command> service processes requests to
+      to update DNS mapping based on DHCP lease change events. The service
+      may run either as a BIND10 module (integrated mode) or as a individual
+      process (stand-alone mode) dependent upon command line arguments. The
+      default is integrated mode.  Stand alone operation is strictly for
+      development purposes and is not suited for production.
     </para>
 
   </refsect1>
@@ -95,7 +99,7 @@
     <title>SEE ALSO</title>
     <para>
       <citerefentry>
-        <refentrytitle>b10-d2</refentrytitle><manvolnum>8</manvolnum>
+        <refentrytitle>b10-dhcp-ddns</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citerefentry>
         <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
@@ -106,7 +110,7 @@
   <refsect1>
     <title>HISTORY</title>
     <para>
-      The <command>b10-d2</command> process was first coded in
+      The <command>b10-dhcp-ddns</command> process was first coded in
       May 2013 by the ISC Kea/Dhcp team.
     </para>
   </refsect1>

+ 0 - 21
src/bin/d2/d2.spec

@@ -1,21 +0,0 @@
-{
-  "module_spec": {
-    "module_name": "D2",
-    "module_description": "DHCP-DDNS process",
-    "config_data": [
-    ],
-    "commands": [
-        {
-            "command_name": "shutdown",
-            "command_description": "Shuts down the D2 process.",
-            "command_args": [
-                {
-                    "item_name": "pid",
-                    "item_type": "integer",
-                    "item_optional": true
-                }
-            ]
-        }
-    ]
-  }
-}

+ 58 - 0
src/bin/d2/d2_controller.cc

@@ -0,0 +1,58 @@
+// 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 <d2/d2_controller.h>
+#include <d2/d2_process.h>
+#include <d2/spec_config.h>
+
+namespace isc {
+namespace d2 {
+
+DControllerBasePtr&
+D2Controller::instance() {
+    // If the instance hasn't been created yet, create it.  Note this method
+    // must use the base class singleton instance methods.  The base class
+    // must have access to the singleton in order to use it within BIND10 
+    // static function callbacks.
+    if (!getController()) {
+        DControllerBasePtr controller_ptr(new D2Controller());
+        setController(controller_ptr);
+    }
+
+    return (getController());
+}
+
+DProcessBase* D2Controller::createProcess() {
+    // Instantiate and return an instance of the D2 application process. Note
+    // that the process is passed the controller's io_service.
+    return (new D2Process(getName().c_str(), getIOService()));
+}
+
+D2Controller::D2Controller()
+    : DControllerBase(D2_MODULE_NAME) {
+    // set the BIND10 spec file either from the environment or
+    // use the production value.
+    if (getenv("B10_FROM_BUILD")) {
+        setSpecFileName(std::string(getenv("B10_FROM_BUILD")) +
+            "/src/bin/d2/d2.spec");
+    } else {
+        setSpecFileName(D2_SPECFILE_LOCATION);
+    }
+}
+
+D2Controller::~D2Controller() {
+}
+
+}; // end namespace isc::d2
+}; // end namespace isc

+ 64 - 0
src/bin/d2/d2_controller.h

@@ -0,0 +1,64 @@
+// 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 D2_CONTROLLER_H
+#define D2_CONTROLLER_H
+
+#include <d2/d_controller.h>
+
+namespace isc {
+namespace d2 {
+
+/// @brief Process Controller for D2 Process
+/// This class is the DHCP-DDNS specific derivation of DControllerBase. It 
+/// creates and manages an instance of the DHCP-DDNS application process, 
+/// D2Process.  
+/// @TODO Currently, this class provides only the minimum required specialized
+/// behavior to run the DHCP-DDNS service. It may very well expand as the 
+/// service implementation evolves.  Some thought was given to making 
+/// DControllerBase a templated class but the labor savings versus the
+/// potential number of virtual methods which may be overridden didn't seem
+/// worth the clutter at this point. 
+class D2Controller : public DControllerBase {
+public:
+    /// @brief Static singleton instance method. This method returns the
+    /// base class singleton instance member.  It instantiates the singleton 
+    /// and sets the base class instance member upon first invocation. 
+    ///
+    /// @return returns the pointer reference to the singleton instance.
+    static DControllerBasePtr& instance();
+
+    /// @brief Destructor.
+    virtual ~D2Controller();
+
+private:
+    /// @brief Creates an instance of the DHCP-DDNS specific application 
+    /// process.  This method is invoked during the process initialization
+    /// step of the controller launch.
+    ///  
+    /// @return returns a DProcessBase* to the application process created.
+    /// Note the caller is responsible for destructing the process. This
+    /// is handled by the base class, which wraps this pointer with a smart
+    /// pointer.
+    virtual DProcessBase* createProcess();
+
+    /// @brief Constructor is declared private to maintain the integrity of
+    /// the singleton instance.
+    D2Controller();
+};
+
+}; // namespace isc::d2
+}; // namespace isc
+
+#endif

+ 6 - 1
src/bin/d2/d2_log.cc

@@ -19,7 +19,12 @@
 namespace isc {
 namespace d2 {
 
-isc::log::Logger d2_logger("d2");
+/// @brief Defines the service name which is used in the controller constructor
+/// and ultimately defines the BIND10 module name.
+const char* const D2_MODULE_NAME = "b10-dhpc-ddns";
+
+/// @brief Defines the logger used within D2.
+isc::log::Logger d2_logger(D2_MODULE_NAME);
 
 } // namespace d2
 } // namespace isc

+ 5 - 0
src/bin/d2/d2_log.h

@@ -22,12 +22,17 @@
 namespace isc {
 namespace d2 {
 
+/// @brief Defines the executable name, ultimately this is the BIND10 module 
+/// name.
+extern const char* const D2_MODULE_NAME;
+
 /// Define the logger for the "d2" module part of b10-d2.  We could define
 /// a logger in each file, but we would want to define a common name to avoid
 /// spelling mistakes, so it is just one small step from there to define a
 /// module-common logger.
 extern isc::log::Logger d2_logger;
 
+
 } // namespace d2
 } // namespace isc
 

+ 64 - 4
src/bin/d2/d2_messages.mes

@@ -15,19 +15,23 @@
 $NAMESPACE isc::d2
 
 % D2CTL_STARTING DHCP-DDNS controller starting, pid: %1
-This is an informational message issued when controller for DHCP-DDNS 
+This is an informational message issued when controller for DHCP-DDNS
 service first starts.
 
 % D2CTL_STOPPING DHCP-DDNS controller is exiting
-This is an informational message issued when the controller is exiting 
+This is an informational message issued when the controller is exiting
 following a shut down (normal or otherwise) of the DDHCP-DDNS process.
 
 % D2PRC_SHUTDOWN DHCP-DDNS process is performing a normal shut down
 This is a debug message issued when the service process has been instructed
 to shut down by the controller.
 
-% D2PRC_RUN_ENTER process has entered the event loop
-This is a debug message issued when the D2 process enters it's
+% D2PRC_PROCESS_INIT DHCP-DDNS application init invoked
+This is a debug message issued when the D2 process enters its
+init method. 
+
+% D2PRC_RUN_ENTER process has entered the event loop 
+This is a debug message issued when the D2 process enters its
 run method. 
 
 % D2PRC_RUN_EXIT process is exiting the event loop
@@ -46,3 +50,59 @@ has been invoked.
 This is a debug message issued when the D2 process command method
 has been invoked.
 
+% D2CTL_INIT_PROCESS initializing application proces 
+This debug message is issued just before the controller attempts
+to create and initialize its process instance.
+
+% D2CTL_SESSION_FAIL failed to establish BIND 10 session: %1
+The controller has failed to establish communication with the rest of BIND
+10 and will exit. 
+
+% D2CTL_DISCONNECT_FAIL failed to disconnect from BIND 10 session: %1
+This message indicates that while shutting down, the DHCP-DDNS controller 
+encountered an error terminating communication with the BIND10. The service 
+will still exit.  While theoretically possible, this situation is rather 
+unlikely. 
+
+% D2CTL_STANDALONE skipping message queue, running standalone
+This is a debug message indicating that the controller is running in the
+process in standalone mode. This means it will not connected to the BIND10 
+message queue. Standalone mode is only useful during program development, 
+and should not be used in a production environment.
+
+% D2CTL_RUN_PROCESS starting application proces event loop 
+This debug message is issued just before the controller invokes 
+the application process run method.
+
+% D2CTL_FAILED process failed: %1
+The controller has encountered a fatal error and is terminating.
+The reason for the failure is included in the message.
+
+% D2CTL_CCSESSION_STARTING starting control channel session, specfile: %1
+This debug message is issued just before the controller attempts
+to establish a session with the BIND 10 control channel.
+
+% D2CTL_CCSESSION_ENDING ending control channel session
+This debug message is issued just before the controller attempts
+to disconnect from its session with the BIND 10 control channel.
+
+% D2CTL_CONFIG_STUB configuration stub handler called 
+This debug message is issued when the dummy handler for configuration
+events is called.  This only happens during intial startup.
+
+% D2CTL_CONFIG_LOAD_FAIL failed to load configuration: %1
+This critical error message indicates that the initial process 
+configuration has failed. The service will start, but will not
+process requests until the configuration has been corrected.
+
+% D2CTL_COMMAND_RECEIVED received command %1, arguments: %2
+A debug message listing the command (and possible arguments) received
+from the BIND 10 control system by the controller.
+
+% D2CTL_NOT_RUNNING The application process instance is not running
+A warning message is issued when an attempt is made to shut down the
+the process when it is not running.
+
+% D2CTL_CONFIG_UPDATE updated configuration received: %1
+A debug message indicating that the controller has received an
+updated configuration from the BIND 10 configuration system.

+ 16 - 16
src/bin/d2/d2_process.cc

@@ -21,7 +21,7 @@ using namespace asio;
 namespace isc {
 namespace d2 {
 
-D2Process::D2Process(const char* name, IOServicePtr io_service) 
+D2Process::D2Process(const char* name, IOServicePtr io_service)
     : DProcessBase(name, io_service) {
 };
 
@@ -29,12 +29,12 @@ void
 D2Process::init() {
 };
 
-int
+void
 D2Process::run() {
     // Until shut down or an fatal error occurs, wait for and
     // execute a single callback. This is a preliminary implementation
     // that is likely to evolve as development progresses.
-    // To use run(), the "managing" layer must issue an io_service::stop 
+    // To use run(), the "managing" layer must issue an io_service::stop
     // or the call to run will continue to block, and shutdown will not
     // occur.
     LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2PRC_RUN_ENTER);
@@ -44,46 +44,46 @@ D2Process::run() {
             io_service->run_one();
         } catch (const std::exception& ex) {
             LOG_FATAL(d2_logger, D2PRC_FAILED).arg(ex.what());
-            return (EXIT_FAILURE); 
+            isc_throw (DProcessBaseError,
+                       "Process run method failed: " << ex.what());
         }
     }
 
     LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2PRC_RUN_EXIT);
-    return (EXIT_SUCCESS);
 };
 
-int 
+void
 D2Process::shutdown() {
     LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2PRC_SHUTDOWN);
     setShutdownFlag(true);
-    return (0);
-}    
+}
 
-isc::data::ConstElementPtr 
+isc::data::ConstElementPtr
 D2Process::configure(isc::data::ConstElementPtr config_set) {
     // @TODO This is the initial implementation which simply accepts
-    // any content in config_set as valid.  This is sufficient to 
+    // any content in config_set as valid.  This is sufficient to
     // allow participation as a BIND10 module, while D2 configuration support
     // is being developed.
-    LOG_DEBUG(d2_logger, DBGLVL_TRACE_BASIC, 
+    LOG_DEBUG(d2_logger, DBGLVL_TRACE_BASIC,
               D2PRC_CONFIGURE).arg(config_set->str());
 
     return (isc::config::createAnswer(0, "Configuration accepted."));
 }
 
-isc::data::ConstElementPtr 
+isc::data::ConstElementPtr
 D2Process::command(const std::string& command, isc::data::ConstElementPtr args){
     // @TODO This is the initial implementation.  If and when D2 is extended
     // to support its own commands, this implementation must change. Otherwise
-    // it should reject all commands as it does now. 
-    LOG_DEBUG(d2_logger, DBGLVL_TRACE_BASIC, 
+    // it should reject all commands as it does now.
+    LOG_DEBUG(d2_logger, DBGLVL_TRACE_BASIC,
               D2PRC_COMMAND).arg(command).arg(args->str());
 
-    return (isc::config::createAnswer(1, "Unrecognized command:" + command));
+    return (isc::config::createAnswer(COMMAND_INVALID, "Unrecognized command: "
+                                      + command));
 }
 
 D2Process::~D2Process() {
 };
 
-}; // namespace isc::d2 
+}; // namespace isc::d2
 }; // namespace isc

+ 37 - 34
src/bin/d2/d2_process.h

@@ -20,79 +20,82 @@
 namespace isc {
 namespace d2 {
 
-/// @brief @TODO DHCP-DDNS Application Process 
+/// @brief DHCP-DDNS Application Process
 ///
-/// D2Process provides the top level application logic for DHCP-driven DDNS 
-/// update processing.  It provides the asynchronous event processing required 
-/// to receive DNS mapping change requests and carry them out.   
+/// D2Process provides the top level application logic for DHCP-driven DDNS
+/// update processing.  It provides the asynchronous event processing required
+/// to receive DNS mapping change requests and carry them out.
 /// It implements the DProcessBase interface, which structures it such that it
-/// is a managed "application", controlled by a management layer. 
+/// is a managed "application", controlled by a management layer.
 
 class D2Process : public DProcessBase {
 public:
     /// @brief Constructor
     ///
     /// @param name name is a text label for the process. Generally used
-    /// in log statements, but otherwise arbitrary. 
+    /// in log statements, but otherwise arbitrary.
     /// @param io_service is the io_service used by the caller for
     /// asynchronous event handling.
     ///
-    /// @throw DProcessBaseError is io_service is NULL. 
+    /// @throw DProcessBaseError is io_service is NULL.
     D2Process(const char* name, IOServicePtr io_service);
 
-    /// @brief Will be used after instantiation to perform initialization 
-    /// unique to D2. This will likely include interactions with QueueMgr and 
-    /// UpdateMgr, to prepare for request receipt and processing.
+    /// @brief Will be used after instantiation to perform initialization
+    /// unique to D2. @TODO This will likely include interactions with
+    /// QueueMgr and UpdateMgr, to prepare for request receipt and processing.
+    /// Current implementation successfully does nothing.
+    /// @throw throws a DProcessBaseError if the initialization fails.
     virtual void init();
 
-    /// @brief Implements the process's event loop. 
-    /// The initial implementation is quite basic, surrounding calls to 
+    /// @brief Implements the process's event loop.
+    /// The initial implementation is quite basic, surrounding calls to
     /// io_service->runOne() with a test of the shutdown flag.
-    /// Once invoked, the method will continue until the process itself is 
-    /// exiting due to a request to shutdown or some anomaly forces an exit.   
-    /// @return  returns 0 upon a successful, "normal" termination, non
-    /// zero to indicate an abnormal termination.    
-    virtual int run();
+    /// Once invoked, the method will continue until the process itself is
+    /// exiting due to a request to shutdown or some anomaly forces an exit.
+    /// @throw throws a DProcessBaseError if an error is encountered.
+    virtual void run();
 
-    // @TODO need brief
-    virtual int shutdown();
+    /// @brief Implements the process's shutdown processing. When invoked, it
+    /// should ensure that the process gracefully exits the run method.
+    /// Current implementation simply sets the shutdown flag monitored by the
+    /// run method. @TODO this may need to expand as the implementation evolves.
+    /// @throw throws a DProcessBaseError if an error is encountered.
+    virtual void shutdown();
 
-    // @TODO need brief
-    /// @brief Processes the given configuration. 
-    /// 
+    /// @brief Processes the given configuration.
+    ///
     /// This method may be called multiple times during the process lifetime.
     /// Certainly once during process startup, and possibly later if the user
     /// alters configuration. This method must not throw, it should catch any
     /// processing errors and return a success or failure answer as described
-    /// below. 
+    /// below.
     ///
     /// @param config_set a new configuration (JSON) for the process
     /// @return an Element that contains the results of configuration composed
     /// of an integer status value (0 means successful, non-zero means failure),
-    /// and a string explanation of the outcome. 
+    /// and a string explanation of the outcome.
     virtual isc::data::ConstElementPtr configure(isc::data::ConstElementPtr
                                                  config_set);
 
-    // @TODO need brief
-    /// @brief Processes the given command. 
-    /// 
-    /// This method is called to execute any custom commands supported by the 
-    /// process. This method must not throw, it should catch any processing 
+    /// @brief Processes the given command.
+    ///
+    /// This method is called to execute any custom commands supported by the
+    /// process. This method must not throw, it should catch any processing
     /// errors and return a success or failure answer as described below.
     ///
     /// @param command is a string label representing the command to execute.
     /// @param args is a set of arguments (if any) required for the given
-    /// command. 
+    /// command.
     /// @return an Element that contains the results of command composed
     /// of an integer status value (0 means successful, non-zero means failure),
-    /// and a string explanation of the outcome.  
-    virtual isc::data::ConstElementPtr command(const std::string& command, 
+    /// and a string explanation of the outcome.
+    virtual isc::data::ConstElementPtr command(const std::string& command,
                                                isc::data::ConstElementPtr args);
-    // @TODO need brief
+    /// @brief Destructor
     virtual ~D2Process();
 };
 
-}; // namespace isc::d2 
+}; // namespace isc::d2
 }; // namespace isc
 
 #endif

+ 427 - 0
src/bin/d2/d_controller.cc

@@ -0,0 +1,427 @@
+// 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 <d2/d2_log.h>
+#include <d2/d_controller.h>
+#include <exceptions/exceptions.h>
+#include <log/logger_support.h>
+
+#include <sstream>
+
+namespace isc {
+namespace d2 {
+
+DControllerBasePtr DControllerBase::controller_;
+
+// Note that the constructor instantiates the controller's primary IOService.
+DControllerBase::DControllerBase(const char* name)
+    : name_(name), stand_alone_(false), verbose_(false),
+    spec_file_name_(""), io_service_(new isc::asiolink::IOService()){
+}
+
+void
+DControllerBase::setController(const DControllerBasePtr& controller) {
+    if (controller_) {
+        // This shouldn't happen, but let's make sure it can't be done.
+        // It represents a programmatic error.
+        isc_throw (DControllerBaseError,
+                "Multiple controller instances attempted.");
+    }
+
+    controller_ = controller;
+}
+
+void
+DControllerBase::launch(int argc, char* argv[]) {
+    // Step 1 is to parse the command line arguments.
+    try {
+        parseArgs(argc, argv);
+    } catch (const InvalidUsage& ex) {
+        usage(ex.what());
+        throw; // rethrow it
+    }
+
+    // Now that we know what the mode flags are, we can init logging.
+    // If standalone is enabled, do not buffer initial log messages
+    isc::log::initLogger(name_,
+                         ((verbose_ && stand_alone_)
+                          ? isc::log::DEBUG : isc::log::INFO),
+                         isc::log::MAX_DEBUG_LEVEL, NULL, !stand_alone_);
+
+    LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2CTL_STARTING).arg(getpid());
+    try {
+        // Step 2 is to create and initialize the application process object.
+        initProcess();
+    } catch (const std::exception& ex) {
+        LOG_FATAL(d2_logger, D2CTL_INIT_PROCESS).arg(ex.what());
+        isc_throw (ProcessInitError, 
+                   "Application Process initialization failed: " << ex.what());
+    }
+
+    // Next we connect if we are running integrated.
+    if (stand_alone_) {
+        LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2CTL_STANDALONE);
+    } else {
+        try {
+            establishSession();
+        } catch (const std::exception& ex) {
+            LOG_FATAL(d2_logger, D2CTL_SESSION_FAIL).arg(ex.what());
+            isc_throw (SessionStartError, 
+                       "Session start up failed: " << ex.what());
+        }
+    }
+
+    // Everything is clear for launch, so start the application's
+    // event loop.
+    try {
+        runProcess();
+    } catch (const std::exception& ex) {
+        LOG_FATAL(d2_logger, D2CTL_FAILED).arg(ex.what());
+        isc_throw (ProcessRunError, 
+                   "Application process event loop failed: " << ex.what());
+    }
+
+    // If running integrated, disconnect.
+    if (!stand_alone_) {
+        try {
+            disconnectSession();
+        } catch (const std::exception& ex) {
+            LOG_ERROR(d2_logger, D2CTL_DISCONNECT_FAIL).arg(ex.what());
+            isc_throw (SessionEndError, "Session end failed: " << ex.what());
+        }
+    }
+
+    // All done, so bail out.
+    LOG_INFO(d2_logger, D2CTL_STOPPING);
+}
+
+
+void
+DControllerBase::parseArgs(int argc, char* argv[])
+{
+    // Iterate over the given command line options. If its a stock option
+    // ("s" or "v") handle it here.  If its a valid custom option, then
+    // invoke customOption.
+    int ch;
+    opterr = 0;
+    optind = 1;
+    std::string opts(":vs" + getCustomOpts());
+    while ((ch = getopt(argc, argv, opts.c_str())) != -1) {
+        switch (ch) {
+        case 'v':
+            // Enables verbose logging.
+            verbose_ = true;
+            break;
+
+        case 's':
+            // Enables stand alone or "BINDLESS" operation.
+            stand_alone_ = true;
+            break;
+
+        case '?': {
+            // We hit an invalid option.
+            isc_throw(InvalidUsage, "unsupported option: [" 
+                      << static_cast<char>(optopt) << "] "
+                      << (!optarg ? "" : optarg));
+
+            break;
+            }
+
+        default:
+            // We hit a valid custom option
+            if (!customOption(ch, optarg)) {
+                // This would be a programmatic error.
+                isc_throw(InvalidUsage, " Option listed but implemented?: [" 
+                          << static_cast<char>(ch) << "] "
+                          << (!optarg ? "" : optarg));
+            }
+            break;
+        }
+    }
+
+    // There was too much information on the command line.
+    if (argc > optind) {
+        isc_throw(InvalidUsage, "extraneous command line information");
+    }
+}
+
+bool
+DControllerBase::customOption(int /* option */, char* /*optarg*/)
+{
+    // Default implementation returns false.
+    return (false);
+}
+
+void
+DControllerBase::initProcess() {
+    LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2CTL_INIT_PROCESS);
+
+    // Invoke virtual method to instantiate the application process.
+    try {
+        process_.reset(createProcess());
+    } catch (const std::exception& ex) {
+        isc_throw(DControllerBaseError, std::string("createProcess failed: ")
+                  + ex.what());
+    }
+
+    // This is pretty unlikely, but will test for it just to be safe..
+    if (!process_) {
+        isc_throw(DControllerBaseError, "createProcess returned NULL");
+    }
+
+    // Invoke application's init method (Note this call should throw
+    // DProcessBaseError if it fails).
+    process_->init();
+}
+
+void
+DControllerBase::establishSession() {
+    LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2CTL_CCSESSION_STARTING)
+              .arg(spec_file_name_);
+
+    // Create the BIND10 command control session with the our IOService.
+    cc_session_ = SessionPtr(new isc::cc::Session(
+                             io_service_->get_io_service()));
+
+    // Create the BIND10 config session with the stub configuration handler.
+    // This handler is internally invoked by the constructor and on success
+    // the constructor updates the current session with the configuration that
+    // had been committed in the previous session. If we do not install
+    // the dummy handler, the previous configuration would be lost.
+    config_session_ = ModuleCCSessionPtr(new isc::config::ModuleCCSession(
+                                         spec_file_name_, *cc_session_,
+                                         dummyConfigHandler, commandHandler,
+                                         false));
+    // Enable configuration even processing.
+    config_session_->start();
+
+    // We initially create ModuleCCSession() with a dummy configHandler, as
+    // the session module is too eager to send partial configuration.
+    // Replace the dummy config handler with the real handler.
+    config_session_->setConfigHandler(configHandler);
+
+    // Call the real configHandler with the full configuration retrieved
+    // from the config session.
+    isc::data::ConstElementPtr answer = configHandler(
+                                            config_session_->getFullConfig());
+
+    // Parse the answer returned from the configHandler.  Log the error but
+    // keep running. This provides an opportunity for the user to correct
+    // the configuration dynamically.
+    int ret = 0;
+    isc::data::ConstElementPtr comment = isc::config::parseAnswer(ret, answer);
+    if (ret) {
+        LOG_ERROR(d2_logger, D2CTL_CONFIG_LOAD_FAIL).arg(comment->str());
+    }
+
+    // Lastly, call onConnect. This allows deriving class to execute custom
+    // logic predicated by session connect.
+    onSessionConnect();
+}
+
+void
+DControllerBase::runProcess() {
+    LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2CTL_RUN_PROCESS);
+    if (!process_) {
+        // This should not be possible.
+        isc_throw(DControllerBaseError, "Process not initialized");
+    }
+
+    // Invoke the application process's run method. This may throw
+    // DProcessBaseError
+    process_->run();
+}
+
+void DControllerBase::disconnectSession() {
+    LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2CTL_CCSESSION_ENDING);
+
+    // Call virtual onDisconnect. Allows deriving class to execute custom
+    // logic prior to session loss.
+    onSessionDisconnect();
+
+    // Destroy the BIND10 config session.
+    if (config_session_) {
+        config_session_.reset();
+    }
+
+    // Destroy the BIND10 command and control session.
+    if (cc_session_) {
+        cc_session_->disconnect();
+        cc_session_.reset();
+    }
+}
+
+isc::data::ConstElementPtr
+DControllerBase::dummyConfigHandler(isc::data::ConstElementPtr) {
+    LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2CTL_CONFIG_STUB);
+    return (isc::config::createAnswer(0, "Configuration accepted."));
+}
+
+isc::data::ConstElementPtr
+DControllerBase::configHandler(isc::data::ConstElementPtr new_config) {
+
+    LOG_DEBUG(d2_logger, DBGLVL_COMMAND, D2CTL_CONFIG_UPDATE)
+            .arg(new_config->str());
+
+    if (!controller_) {
+        // This should never happen as we install the handler after we
+        // instantiate the server.
+        isc::data::ConstElementPtr answer =
+            isc::config::createAnswer(1, "Configuration rejected,"
+                                   " Controller has not been initialized.");
+        return (answer);
+    }
+
+    // Invoke the instance method on the controller singleton.
+    return (controller_->updateConfig(new_config));
+}
+
+// Static callback which invokes non-static handler on singleton
+isc::data::ConstElementPtr
+DControllerBase::commandHandler(const std::string& command,
+                                isc::data::ConstElementPtr args) {
+
+    LOG_DEBUG(d2_logger, DBGLVL_COMMAND, D2CTL_COMMAND_RECEIVED)
+              .arg(command).arg(args->str());
+
+    if (!controller_ )  {
+        // This should never happen as we install the handler after we
+        // instantiate the server.
+        isc::data::ConstElementPtr answer =
+            isc::config::createAnswer(1, "Command rejected,"
+                                   " Controller has not been initialized.");
+        return (answer);
+    }
+
+    // Invoke the instance method on the controller singleton.
+    return (controller_->executeCommand(command, args));
+}
+
+isc::data::ConstElementPtr
+DControllerBase::updateConfig(isc::data::ConstElementPtr new_config) {
+    isc::data::ConstElementPtr full_config;
+    if (stand_alone_) {
+        // @TODO Until there is a configuration manager to provide retrieval
+        // we'll just assume the incoming config is the full configuration set.
+        // It may also make more sense to isolate the controller from the
+        // configuration manager entirely. We could do something like
+        // process_->getFullConfig() here for stand-alone mode?
+        full_config = new_config;
+    } else {
+        if (!config_session_) {
+            // That should never happen as we install config_handler
+            // after we instantiate the server.
+            isc::data::ConstElementPtr answer =
+                    isc::config::createAnswer(1, "Configuration rejected,"
+                                              " Session has not started.");
+            return (answer);
+        }
+
+        // Let's get the existing configuration.
+        full_config = config_session_->getFullConfig();
+    }
+
+    // The configuration passed to this handler function is partial.
+    // In other words, it just includes the values being modified.
+    // In the same time, there may be dependencies between various
+    // configuration parsers. For example: the option value can
+    // be set if the definition of this option is set. If someone removes
+    // an existing option definition then the partial configuration that
+    // removes that definition is triggered while a relevant option value
+    // may remain configured. This eventually results in the
+    // configuration being in the inconsistent state.
+    // In order to work around this problem we need to merge the new
+    // configuration with the existing (full) configuration.
+
+    // Let's create a new object that will hold the merged configuration.
+    boost::shared_ptr<isc::data::MapElement>
+                            merged_config(new isc::data::MapElement());
+
+    // Merge an existing and new configuration.
+    merged_config->setValue(full_config->mapValue());
+    isc::data::merge(merged_config, new_config);
+
+    // Send the merged configuration to the application.
+    return (process_->configure(merged_config));
+}
+
+
+isc::data::ConstElementPtr
+DControllerBase::executeCommand(const std::string& command,
+                            isc::data::ConstElementPtr args) {
+    // Shutdown is universal.  If its not that, then try it as
+    // an custom command supported by the derivation.  If that
+    // doesn't pan out either, than send to it the application
+    // as it may be supported there.
+    isc::data::ConstElementPtr answer;
+    if (command.compare(SHUT_DOWN_COMMAND) == 0) {
+        answer = shutdown();
+    } else {
+        // It wasn't shutdown, so may be a custom controller command.
+        int rcode = 0;
+        answer = customControllerCommand(command, args);
+        isc::config::parseAnswer(rcode, answer);
+        if (rcode == COMMAND_INVALID)
+        {
+            // It wasn't controller command, so may be an application command.
+            answer = process_->command(command,args);
+        }
+    }
+
+    return (answer);
+}
+
+isc::data::ConstElementPtr
+DControllerBase::customControllerCommand(const std::string& command,
+                                     isc::data::ConstElementPtr /* args */) {
+
+    // Default implementation always returns invalid command.
+    return (isc::config::createAnswer(COMMAND_INVALID,
+                                      "Unrecognized command: " + command));
+}
+
+isc::data::ConstElementPtr
+DControllerBase::shutdown() {
+    if (process_) {
+        process_->shutdown();
+    } else {
+        // Not really a failure, but this condition is worth noting. In reality
+        // it should be pretty hard to cause this.
+        LOG_WARN(d2_logger, D2CTL_NOT_RUNNING);
+    }
+
+    return (isc::config::createAnswer(0, "Shutting down."));
+}
+
+void
+DControllerBase::usage(const std::string & text)
+{
+    if (text != "") {
+        std::cerr << "Usage error: " << text << std::endl;
+    }
+
+    std::cerr << "Usage: " << name_ <<  std::endl;
+    std::cerr << "  -v: verbose output" << std::endl;
+    std::cerr << "  -s: stand-alone mode (don't connect to BIND10)"
+              << std::endl;
+
+    std::cerr << getUsageText() << std::endl;
+}
+
+DControllerBase::~DControllerBase() {
+}
+
+}; // namespace isc::d2
+}; // namespace isc

+ 524 - 0
src/bin/d2/d_controller.h

@@ -0,0 +1,524 @@
+// 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 D_CONTROLLER_H
+#define D_CONTROLLER_H
+
+#include <asiolink/asiolink.h>
+#include <cc/data.h>
+#include <cc/session.h>
+#include <config/ccsession.h>
+#include <d2/d2_log.h>
+#include <d2/d_process.h>
+#include <exceptions/exceptions.h>
+#include <log/logger_support.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+
+namespace isc {
+namespace d2 {
+
+/// @brief DControllerBase launch exit status values.  Upon service shutdown
+/// normal or otherwise, the Controller's launch method will return one of
+/// these values.
+
+/// @brief Exception thrown when the command line is invalid.
+class InvalidUsage : public isc::Exception {
+public:
+    InvalidUsage(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief Exception thrown when the application process fails.
+class ProcessInitError: public isc::Exception {
+public:
+    ProcessInitError (const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief Exception thrown when the session start up fails.
+class SessionStartError: public isc::Exception {
+public:
+    SessionStartError (const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief Exception thrown when the application process encounters an 
+/// operation in its event loop (i.e. run method).
+class ProcessRunError: public isc::Exception {
+public:
+    ProcessRunError (const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief Exception thrown when the session end fails.
+class SessionEndError: public isc::Exception {
+public:
+    SessionEndError (const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+
+/// @brief Exception thrown when the controller encounters an operational error.
+class DControllerBaseError : public isc::Exception {
+public:
+    DControllerBaseError (const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+
+/// @brief Defines a shared pointer to DControllerBase.
+class DControllerBase;
+typedef boost::shared_ptr<DControllerBase> DControllerBasePtr;
+
+/// @brief Defines a shared pointer to a Session.
+typedef boost::shared_ptr<isc::cc::Session> SessionPtr;
+
+/// @brief Defines a shared pointer to a ModuleCCSession.
+typedef boost::shared_ptr<isc::config::ModuleCCSession> ModuleCCSessionPtr;
+
+
+/// @brief Application Controller
+///
+/// DControllerBase is an abstract singleton which provides the framework and
+/// services for managing an application process that implements the
+/// DProcessBase interface.  It allows the process to run either in
+/// integrated mode as a BIND10 module or stand-alone. It coordinates command
+/// line argument parsing, process instantiation and initialization, and runtime
+/// control through external command and configuration event handling.
+/// It creates the IOService instance which is used for runtime control
+/// events and passes the IOService into the application process at process
+/// creation.  In integrated mode it is responsible for establishing BIND10
+/// session(s) and passes this IOService into the session creation method(s).
+/// It also provides the callback handlers for command and configuration events
+/// received from the external framework (aka BIND10).  For example, when 
+/// running in integrated mode and a user alters the configuration with the
+/// bindctl tool, BIND10 will emit a configuration message which is sensed by
+/// the controller's IOService. The IOService in turn invokes the configuration
+/// callback, DControllerBase::configHandler().  If the user issues a command
+/// such as shutdown via bindctl,  BIND10 will emit a command message, which is
+/// sensed by controller's IOService which invokes the command callback, 
+/// DControllerBase::commandHandler().
+///
+/// NOTE: Derivations must supply their own static singleton instance method(s)
+/// for creating and fetching the instance. The base class declares the instance
+/// member in order for it to be available for BIND10 callback functions. This
+/// would not be required if BIND10 supported instance method callbacks.   
+class DControllerBase : public boost::noncopyable {
+public:
+    /// @brief Constructor
+    ///
+    /// @param name name is a text label for the controller. Typically this
+    /// would be the BIND10 module name.
+    DControllerBase(const char* name);
+
+    /// @brief Destructor
+    virtual ~DControllerBase();
+
+    /// @brief Acts as the primary entry point into the controller execution
+    /// and provides the outermost application control logic:
+    ///
+    /// 1. parse command line arguments
+    /// 2. instantiate and initialize the application process
+    /// 3. establish BIND10 session(s) if in integrated mode
+    /// 4. start and wait on the application process event loop
+    /// 5. upon event loop completion, disconnect from BIND10 (if needed)
+    /// 6. exit to the caller
+    ///
+    /// It is intended to be called from main() and be given the command line
+    /// arguments. Note this method is deliberately not virtual to ensure the
+    /// proper sequence of events occur.
+    ///
+    /// @param argc  is the number of command line arguments supplied
+    /// @param argv  is the array of string (char *) command line arguments
+    ///
+    /// @throw throws one of the following exceptions:
+    /// InvalidUsage - Indicates invalid command line.
+    /// ProcessInitError  - Failed to create and initialize application
+    /// process object.
+    /// SessionStartError  - Could not connect to BIND10 (integrated mode only).
+    /// ProcessRunError - A fatal error occurred while in the application 
+    /// process event loop.
+    /// SessionEndError - Could not disconnect from BIND10 (integrated mode 
+    /// only).
+    void launch(int argc, char* argv[]);
+
+    /// @brief A dummy configuration handler that always returns success.
+    ///
+    /// This configuration handler does not perform configuration
+    /// parsing and always returns success. A dummy handler should
+    /// be installed using \ref isc::config::ModuleCCSession ctor
+    /// to get the initial configuration. This initial configuration
+    /// comprises values for only those elements that were modified
+    /// the previous session. The D2 configuration parsing can't be
+    /// used to parse the initial configuration because it may need the
+    /// full configuration to satisfy dependencies between the
+    /// various configuration values. Installing the dummy handler
+    /// that guarantees to return success causes initial configuration
+    /// to be stored for the session being created and that it can
+    /// be later accessed with \ref isc::ConfigData::getFullConfig.
+    ///
+    /// @param new_config new configuration.
+    ///
+    /// @return success configuration status.
+    static isc::data::ConstElementPtr
+    dummyConfigHandler(isc::data::ConstElementPtr new_config);
+
+    /// @brief A callback for handling all incoming configuration updates.
+    ///
+    /// As a pointer to this method is used as a callback in ASIO for
+    /// ModuleCCSession, it has to be static.  It acts as a wrapper around
+    /// the virtual instance method, updateConfig.
+    ///
+    /// @param new_config textual representation of the new configuration
+    ///
+    /// @return status of the config update
+    static isc::data::ConstElementPtr
+    configHandler(isc::data::ConstElementPtr new_config);
+
+    /// @brief A callback for handling all incoming commands.
+    ///
+    /// As a pointer to this method is used as a callback in ASIO for
+    /// ModuleCCSession, it has to be static.  It acts as a wrapper around
+    /// the virtual instance method, executeCommand.
+    ///
+    /// @param command textual representation of the command
+    /// @param args parameters of the command
+    ///
+    /// @return status of the processed command
+    static isc::data::ConstElementPtr
+    commandHandler(const std::string& command, isc::data::ConstElementPtr args);
+
+    /// @brief Instance method invoked by the configuration event handler and
+    /// which processes the actual configuration update.  Provides behavioral
+    /// path for both integrated and stand-alone modes. The current
+    /// implementation will merge the configuration update into the existing
+    /// configuration and then invoke the application process' configure method.
+    ///
+    /// @TODO This implementation is will evolve as the D2 configuration
+    /// management task is implemented (trac #2957).
+    ///
+    /// @param  new_config is the new configuration
+    ///
+    /// @return returns an Element that contains the results of configuration
+    /// update composed of an integer status value (0 means successful,
+    /// non-zero means failure), and a string explanation of the outcome.
+    virtual isc::data::ConstElementPtr
+    updateConfig(isc::data::ConstElementPtr new_config);
+
+
+    /// @brief Instance method invoked by the command event handler and  which
+    /// processes the actual command directive.
+    ///
+    /// It supports the execution of:
+    ///
+    ///   1. Stock controller commands - commands common to all DControllerBase
+    /// derivations.  Currently there is only one, the shutdown command.
+    ///
+    ///   2. Custom controller commands - commands that the deriving controller
+    /// class implements.  These commands are executed by the deriving
+    /// controller.
+    ///
+    ///   3. Custom application commands - commands supported by the application
+    /// process implementation.  These commands are executed by the application
+    /// process.
+    ///
+    /// @param command is a string label representing the command to execute.
+    /// @param args is a set of arguments (if any) required for the given
+    /// command.
+    ///
+    /// @return an Element that contains the results of command composed
+    /// of an integer status value and a string explanation of the outcome.
+    /// The status value is one of the following:
+    ///   D2::COMMAND_SUCCESS - Command executed successfully
+    ///   D2::COMMAND_ERROR - Command is valid but suffered an operational
+    ///   failure.
+    ///   D2::COMMAND_INVALID - Command is not recognized as valid be either
+    ///   the controller or the application process.
+    virtual isc::data::ConstElementPtr
+    executeCommand(const std::string& command, isc::data::ConstElementPtr args);
+
+protected:
+    /// @brief Virtual method that provides derivations the opportunity to
+    /// support additional command line options.  It is invoked during command
+    /// line argument parsing (see parseArgs method) if the option is not
+    /// recognized as a stock DControllerBase option.
+    ///
+    /// @param option is the option "character" from the command line, without
+    /// any prefixing hyphen(s)
+    /// @optarg optarg is the argument value (if one) associated with the option
+    ///
+    /// @return must return true if the option was valid, false is it is
+    /// invalid. (Note the default implementation always returns false.)
+    virtual bool customOption(int option, char *optarg);
+
+    /// @brief Abstract method that is responsible for instantiating the
+    /// application process object. It is invoked by the controller after
+    /// command line argument parsing as part of the process initialization
+    /// (see initProcess method).
+    ///
+    /// @return returns a pointer to the new process object (DProcessBase*)
+    /// or NULL if the create fails.
+    /// Note this value is subsequently wrapped in a smart pointer.
+    virtual DProcessBase* createProcess() = 0;
+
+    /// @brief Virtual method that provides derivations the opportunity to
+    /// support custom external commands executed by the controller.  This
+    /// method is invoked by the processCommand if the received command is
+    /// not a stock controller command.
+    ///
+    /// @param command is a string label representing the command to execute.
+    /// @param args is a set of arguments (if any) required for the given
+    /// command.
+    ///
+    /// @return an Element that contains the results of command composed
+    /// of an integer status value and a string explanation of the outcome.
+    /// The status value is one of the following:
+    ///   D2::COMMAND_SUCCESS - Command executed successfully
+    ///   D2::COMMAND_ERROR - Command is valid but suffered an operational
+    ///   failure.
+    ///   D2::COMMAND_INVALID - Command is not recognized as a valid custom
+    ///   controller command.
+    virtual isc::data::ConstElementPtr customControllerCommand(
+            const std::string& command, isc::data::ConstElementPtr args);
+
+    /// @brief Virtual method which is invoked after the controller successfully
+    /// establishes BIND10 connectivity.  It provides an opportunity for the
+    /// derivation to execute any custom behavior associated with session
+    /// establishment.
+    ///
+    /// Note, it is not called  when running stand-alone.
+    ///
+    /// @throw should throw a DControllerBaseError if it fails.
+    virtual void onSessionConnect(){};
+
+    /// @brief Virtual method which is invoked as the first action taken when
+    /// the controller is terminating the session(s) with BIND10.  It provides
+    /// an opportunity for the derivation to execute any custom behavior
+    /// associated with session termination.
+    ///
+    /// Note, it is not called  when running stand-alone.
+    ///
+    /// @throw should throw a DControllerBaseError if it fails.
+    virtual void onSessionDisconnect(){};
+
+    /// @brief Virtual method which can be used to contribute derivation
+    /// specific usage text.  It is invoked by the usage() method under
+    /// invalid usage conditions.
+    ///
+    /// @return returns the desired text.
+    virtual const std::string getUsageText() const {
+        return ("");
+    }
+
+    /// @brief Virtual method which returns a string containing the option
+    /// letters for any custom command line options supported by the derivation.
+    /// These are added to the stock options of "s" and "v" during command
+    /// line interpretation.
+    ///
+    /// @return returns a string containing the custom option letters.
+    virtual const std::string getCustomOpts() const {
+        return ("");
+    }
+
+    /// @brief Supplies the controller name.
+    ///
+    /// @return returns the controller name string
+    const std::string getName() const {
+        return (name_);
+    }
+
+    /// @brief Supplies whether or not the controller is in stand alone mode.
+    ///
+    /// @return returns true if in stand alone mode, false otherwise
+    bool isStandAlone() const {
+        return (stand_alone_);
+    }
+
+    /// @brief Method for enabling or disabling stand alone mode.
+    ///
+    /// @param value is the new value to assign the flag.
+    void setStandAlone(bool value) {
+        stand_alone_ = value;
+    }
+
+    /// @brief Supplies whether or not verbose logging is enabled.
+    ///
+    /// @return returns true if verbose logging is enabled.
+    bool isVerbose() const {
+        return (verbose_);
+    }
+
+    /// @brief Method for enabling or disabling verbose logging.
+    ///
+    /// @param value is the new value to assign the flag.
+    void setVerbose(bool value) {
+        verbose_ = value;
+    }
+
+    /// @brief Getter for fetching the controller's IOService
+    ///
+    /// @return returns a pointer reference to the IOService.
+    IOServicePtr& getIOService() {
+        return (io_service_);
+    }
+
+    /// @brief Getter for fetching the name of the controller's BIND10 spec
+    /// file.
+    ///
+    /// @return returns the file name string.
+    const std::string getSpecFileName() const {
+        return (spec_file_name_);
+    }
+
+    /// @brief Setter for setting the name of the controller's BIND10 spec file.
+    ///
+    /// @param value is the file name string.
+    void setSpecFileName(const std::string& spec_file_name) {
+        spec_file_name_ = spec_file_name;
+    }
+
+    /// @brief Static getter which returns the singleton instance.
+    ///
+    /// @return returns a pointer reference to the private singleton instance
+    /// member.
+    static DControllerBasePtr& getController() {
+        return (controller_);
+    }
+
+    /// @brief Static setter which sets the singleton instance.
+    ///
+    /// @param controller is a pointer to the singleton instance.
+    ///
+    /// @throw throws DControllerBase error if an attempt is made to set the
+    /// instance a second time.
+    static void setController(const DControllerBasePtr& controller);
+
+private:
+    /// @brief Processes the command line arguments. It is the first step
+    /// taken after the controller has been launched.  It combines the stock
+    /// list of options with those returned by getCustomOpts(), and uses
+    /// cstdlib's getopt to loop through the command line.  The stock options
+    /// It handles stock options directly, and passes any custom options into
+    /// the customOption method.  Currently there are only two stock options
+    /// -s for stand alone mode, and -v for verbose logging.
+    ///
+    /// @param argc  is the number of command line arguments supplied
+    /// @param argv  is the array of string (char *) command line arguments
+    ///
+    /// @throw throws InvalidUsage when there are usage errors.
+    void parseArgs(int argc, char* argv[]);
+
+    /// @brief Instantiates the application process and then initializes it.
+    /// This is the second step taken during launch, following successful
+    /// command line parsing. It is used to invoke the derivation-specific
+    /// implementation of createProcess, following by an invoking of the
+    /// newly instantiated process's init method.
+    ///
+    /// @throw throws DControllerBaseError or indirectly DProcessBaseError
+    /// if there is a failure creating or initializing the application process.
+    void initProcess();
+
+    /// @brief Establishes connectivity with BIND10.  This method is used
+    /// invoked during launch, if running in integrated mode, following
+    /// successful process initialization.  It is responsible for establishing
+    /// the BIND10 control and config sessions. During the session creation,
+    /// it passes in the controller's IOService and the callbacks for command
+    /// directives and config events.  Lastly, it will invoke the onConnect
+    /// method providing the derivation an opportunity to execute any custom
+    /// logic associated with session establishment.
+    ///
+    /// @throw the BIND10 framework may throw std::exceptions.
+    void establishSession();
+
+    /// @brief Invokes the application process's event loop,(DBaseProcess::run).
+    /// It is called during launch only after successfully completing the
+    /// requested setup: command line parsing, application initialization,
+    /// and session establishment (if not stand-alone).
+    /// The process event loop is expected to only return upon application
+    /// shutdown either in response to the shutdown command or due to an
+    /// unrecoverable error.
+    ///
+    // @throw throws DControllerBaseError or indirectly DProcessBaseError
+    void runProcess();
+
+    /// @brief Terminates connectivity with BIND10. This method is invoked
+    /// in integrated mode after the application event loop has exited. It
+    /// first calls the onDisconnect method providing the derivation an
+    /// opportunity to execute custom logic if needed, and then terminates the
+    /// BIND10 config and control sessions.
+    ///
+    /// @throw the BIND10 framework may throw std:exceptions.
+    void disconnectSession();
+
+    /// @brief Initiates shutdown procedure.  This method is invoked
+    /// by executeCommand in response to the shutdown command. It will invoke
+    /// the application process's shutdown method, which causes the process to
+    /// exit it's event loop.
+    ///
+    /// @return returns an Element that contains the results of shutdown
+    /// attempt composed of an integer status value (0 means successful,
+    /// non-zero means failure), and a string explanation of the outcome.
+    isc::data::ConstElementPtr shutdown();
+
+    /// @brief Prints the program usage text to std error.
+    ///
+    /// @param text is a string message which will preceded the usage text.
+    /// This is intended to be used for specific usage violation messages.
+    void usage(const std::string& text);
+
+private:
+    /// @brief Text label for the controller. Typically this would be the
+    /// BIND10 module name.
+    std::string name_;
+
+    /// @brief Indicates if the controller stand alone mode is enabled. When
+    /// enabled, the controller will not establish connectivity with BIND10.
+    bool stand_alone_;
+
+    /// @brief Indicates if the verbose logging mode is enabled.
+    bool verbose_;
+
+    /// @brief The absolute file name of the BIND10 spec file.
+    std::string spec_file_name_;
+
+    /// @brief Pointer to the instance of the process.
+    ///
+    /// This is required for config and command handlers to gain access to
+    /// the process
+    DProcessBasePtr process_;
+
+    /// @brief Shared pointer to an IOService object, used for ASIO operations.
+    IOServicePtr io_service_;
+
+    /// @brief Helper session object that represents raw connection to msgq.
+    SessionPtr cc_session_;
+
+    /// @brief Session that receives configuration and commands.
+    ModuleCCSessionPtr config_session_;
+
+    /// @brief Singleton instance value.
+    static DControllerBasePtr controller_;
+
+// DControllerTest is named a friend class to facilitate unit testing while
+// leaving the intended member scopes intact.
+friend class DControllerTest;
+};
+
+}; // namespace isc::d2
+}; // namespace isc
+
+#endif

+ 80 - 50
src/bin/d2/d_process.h

@@ -33,28 +33,33 @@ public:
         isc::Exception(file, line, what) { };
 };
 
+static const int COMMAND_SUCCESS = 0;
+static const int COMMAND_ERROR = 1;
+static const int COMMAND_INVALID = 2;
+static const std::string SHUT_DOWN_COMMAND("shutdown");
+
 /// @brief Application Process Interface
 ///
-/// DProcessBase is an abstract class represents the primary "application" 
-/// level object in a "managed" asynchronous application. It provides a uniform 
-/// interface such that a managing layer can construct, intialize, and start
+/// DProcessBase is an abstract class represents the primary "application"
+/// level object in a "managed" asynchronous application. It provides a uniform
+/// interface such that a managing layer can construct, initialize, and start
 /// the application's event loop.  The event processing is centered around the
-/// use of isc::asiolink::io_service. The io_service is shared between the 
-/// the managing layer and the DProcessBase.  This allows management layer IO 
-/// such as directives to be sensed and handled, as well as processing IO 
-/// activity specific to the application.  In terms of management layer IO,
-/// there are methods shutdown, configuration updates, and commands unique
-/// to the application.  
+/// use of isc::asiolink::io_service. The io_service is shared between the
+/// managing layer and the DProcessBase.  This allows management layer IO such
+/// as directives to be sensed and handled, as well as processing IO activity
+/// specific to the application.  In terms of management layer IO, there are
+/// methods shutdown, configuration updates, and commands unique to the
+/// application.
 class DProcessBase {
 public:
     /// @brief Constructor
     ///
     /// @param name name is a text label for the process. Generally used
-    /// in log statements, but otherwise arbitrary. 
+    /// in log statements, but otherwise arbitrary.
     /// @param io_service is the io_service used by the caller for
     /// asynchronous event handling.
     ///
-    /// @throw DProcessBaseError is io_service is NULL. 
+    /// @throw DProcessBaseError is io_service is NULL.
     DProcessBase(const char* name, IOServicePtr io_service) : name_(name),
         io_service_(io_service), shut_down_flag_(false) {
 
@@ -64,78 +69,103 @@ public:
     };
 
     /// @brief May be used after instantiation to perform initialization unique
-    /// to application. It must be invoked prior to invoking run. This would 
-    /// likely include the creation of additional IO sources and their 
-    /// integration into the io_service. 
-    virtual void init() = 0; 
-
-    /// @brief Implements the process's event loop. In its simplest form it 
-    /// would an invocation io_service_->run().  This method should not exit 
-    /// until the process itself is exiting due to a request to shutdown or 
-    /// some anomaly is forcing an exit.   
-    /// @return  returns EXIT_SUCCESS upon a successful, normal termination, 
-    /// and EXIT_FAILURE to indicate an abnormal termination.    
-    virtual int run() = 0; 
-
-    /// @brief Implements the process's shutdown processing. When invoked, it 
-    /// should ensure that the process gracefully exits the run method. 
-    virtual int shutdown() = 0;
-
-    /// @brief Processes the given configuration. 
-    /// 
+    /// to application. It must be invoked prior to invoking run. This would
+    /// likely include the creation of additional IO sources and their
+    /// integration into the io_service.
+    /// @throw throws DProcessBaseError if the initialization fails.
+    virtual void init() = 0;
+
+    /// @brief Implements the process's event loop. In its simplest form it
+    /// would an invocation io_service_->run().  This method should not exit
+    /// until the process itself is exiting due to a request to shutdown or
+    /// some anomaly is forcing an exit.
+    /// @throw throws DProcessBaseError if an operational error is encountered.
+    virtual void run() = 0;
+
+    /// @brief Implements the process's shutdown processing. When invoked, it
+    /// should ensure that the process gracefully exits the run method.
+    /// The default implementation sets the shutdown flag and stops IOService.
+    /// @throw throws DProcessBaseError if an operational error is encountered.
+    virtual void shutdown() {
+        setShutdownFlag(true);
+        stopIOService();
+    };
+
+    /// @brief Processes the given configuration.
+    ///
     /// This method may be called multiple times during the process lifetime.
     /// Certainly once during process startup, and possibly later if the user
     /// alters configuration. This method must not throw, it should catch any
     /// processing errors and return a success or failure answer as described
-    /// below. 
+    /// below.
     ///
     /// @param config_set a new configuration (JSON) for the process
     /// @return an Element that contains the results of configuration composed
     /// of an integer status value (0 means successful, non-zero means failure),
-    /// and a string explanation of the outcome. 
+    /// and a string explanation of the outcome.
     virtual isc::data::ConstElementPtr configure(isc::data::ConstElementPtr
-                                                 config_set) = 0; 
+                                                 config_set) = 0;
 
-    /// @brief Processes the given command. 
-    /// 
-    /// This method is called to execute any custom commands supported by the 
-    /// process. This method must not throw, it should catch any processing 
+    /// @brief Processes the given command.
+    ///
+    /// This method is called to execute any custom commands supported by the
+    /// process. This method must not throw, it should catch any processing
     /// errors and return a success or failure answer as described below.
     ///
     /// @param command is a string label representing the command to execute.
     /// @param args is a set of arguments (if any) required for the given
-    /// command. 
+    /// command.
     /// @return an Element that contains the results of command composed
     /// of an integer status value (0 means successful, non-zero means failure),
-    /// and a string explanation of the outcome.  
+    /// and a string explanation of the outcome.
     virtual isc::data::ConstElementPtr command(
-            const std::string& command, isc::data::ConstElementPtr args) = 0; 
+            const std::string& command, isc::data::ConstElementPtr args) = 0;
 
-    /// @brief Destructor 
+    /// @brief Destructor
     virtual ~DProcessBase(){};
 
-    bool shouldShutdown() { 
-        return (shut_down_flag_); 
+    /// @brief Checks if the process has been instructed to shut down.
+    ///
+    /// @return returns true if process shutdown flag is true.
+    bool shouldShutdown() {
+        return (shut_down_flag_);
     }
 
-    void setShutdownFlag(bool value) { 
-        shut_down_flag_ = value; 
+    /// @brief Sets the process shut down flag to the given value.
+    ///
+    /// @param value is the new value to assign the flag.
+    void setShutdownFlag(bool value) {
+        shut_down_flag_ = value;
     }
 
-    const std::string& getName() const {
+    /// @brief Fetches the name of the controller.
+    ///
+    /// @return returns a reference the controller's name string.
+    const std::string getName() const {
         return (name_);
     }
 
+    /// @brief Fetches the controller's IOService.
+    ///
+    /// @return returns a reference to the controller's IOService.
     IOServicePtr& getIoService() {
         return (io_service_);
     }
 
+    /// @brief Convenience method for stopping IOservice processing.
+    /// Invoking this will cause the process to exit any blocking 
+    /// IOService method such as run().  No further IO events will be
+    /// processed.
+    void stopIOService() {
+        io_service_->stop();
+    }
+
 private:
-    /// @brief Text label for the process. Generally used in log statements, 
-    /// but otherwise can be arbitrary. 
+    /// @brief Text label for the process. Generally used in log statements,
+    /// but otherwise can be arbitrary.
     std::string name_;
 
-    /// @brief The IOService to be used for asynchronous event handling. 
+    /// @brief The IOService to be used for asynchronous event handling.
     IOServicePtr io_service_;
 
     /// @brief Boolean flag set when shutdown has been requested.
@@ -145,7 +175,7 @@ private:
 /// @brief Defines a shared pointer to DProcessBase.
 typedef boost::shared_ptr<DProcessBase> DProcessBasePtr;
 
-}; // namespace isc::d2 
+}; // namespace isc::d2
 }; // namespace isc
 
 #endif

+ 21 - 0
src/bin/d2/dhcp-ddns.spec

@@ -0,0 +1,21 @@
+{
+  "module_spec": {
+    "module_name": "D2",
+    "module_description": "DHPC-DDNS Service",
+    "config_data": [
+    ],
+    "commands": [
+      {
+        "command_name": "shutdown",
+        "command_description": "Shut down the DHCP-DDNS service",
+        "command_args": [
+          {
+            "item_name": "pid",
+            "item_type": "integer",
+            "item_optional": true
+          }
+        ]
+      }
+    ]
+  }
+}

+ 21 - 68
src/bin/d2/main.cc

@@ -14,6 +14,8 @@
 
 #include <config.h>
 #include <d2/d2_log.h>
+#include <d2/d2_controller.h>
+#include <exceptions/exceptions.h>
 #include <log/logger_support.h>
 #include <log/logger_manager.h>
 
@@ -22,74 +24,25 @@
 using namespace isc::d2;
 using namespace std;
 
-/// This file contains entry point (main() function) for standard DHCP-DDNS 
-/// process, b10-d2, component for BIND10 framework. It parses command-line
-/// arguments and instantiates D2Controller class that is responsible for
-/// establishing connection with msgq (receiving commands and configuration)
-/// and also creating D2Server object as well.
-///
-/// For detailed explanation or relations between main(), D2Controller,
-/// D2Server and other classes, see \ref d2Session.
-
-namespace {
-
-const char* const D2_NAME = "b10-d2";
-
-void
-usage() {
-    cerr << "Usage: " << D2_NAME << " [-v] [-s]" << endl;
-    cerr << "  -s: stand-alone mode (don't connect to BIND10)" << endl;
-    cerr << "  -v: verbose output (only when in stand-alone mode" << endl;
-    exit(EXIT_FAILURE);
-}
-} // end of anonymous namespace
-
-int
-main(int argc, char* argv[]) {
-    int ch;
-
-    // @TODO NOTE these parameters are preliminary only. They are here to
-    // for symmetry with the DHCP servers.  They may or may not
-    // become part of the eventual implementation.
-
-    bool stand_alone = false;  // Should be connect to BIND10 msgq?
-    bool verbose_mode = false; // Should server be verbose?
-
-    while ((ch = getopt(argc, argv, "vsp:")) != -1) {
-        switch (ch) {
-        case 'v':
-            verbose_mode = true;
-            break;
-
-        case 's':
-            stand_alone = true;
-            break;
-
-        default:
-            usage();
-        }
-    }
-
-    // Check for extraneous parameters.
-    if (argc > optind) {
-        usage();
+/// This file contains entry point (main() function) for standard DHCP-DDNS
+/// process, b10-dhcp-ddns, component for BIND10 framework.  It fetches
+/// the D2Controller singleton instance and invokes its launch method.
+/// The exit value of the program will be EXIT_SUCCESS if there were no
+/// errors, EXIT_FAILURE otherwise.
+int main(int argc, char* argv[]) {
+    int ret = EXIT_SUCCESS;
+
+    // Instantiate/fetch the DHCP-DDNS application controller singleton.
+    DControllerBasePtr& controller = D2Controller::instance();
+
+    // Launch the controller passing in command line arguments.
+    // Exit program with the controller's return code.
+    try  {
+        controller->launch(argc, argv);
+    } catch (const isc::Exception& ex) {
+        std::cerr << "Service failed:" << ex.what() << std::endl;
+        ret = EXIT_FAILURE;
     }
 
-    // Initialize logging.  If verbose, we'll use maximum verbosity.
-    // If standalone is enabled, do not buffer initial log messages
-    // Verbose logging is only enabled when in stand alone mode.
-    isc::log::initLogger(D2_NAME,
-                         ((verbose_mode && stand_alone)
-                           ? isc::log::DEBUG : isc::log::INFO),
-                         isc::log::MAX_DEBUG_LEVEL, NULL, !stand_alone);
-
-    LOG_INFO(d2_logger, D2CTL_STARTING);
-
-    // For now we will sleep awhile to simulate doing something.
-    // Without at least a sleep, the process will start, exit and be
-    // restarted by Bind10/Init endlessley in a rapid succession.
-    sleep(1000);
-    LOG_INFO(d2_logger, D2CTL_STOPPING);
-    return (EXIT_SUCCESS);
+    return (ret);
 }
-

+ 6 - 1
src/bin/d2/tests/Makefile.am

@@ -52,10 +52,15 @@ if HAVE_GTEST
 TESTS += d2_unittests
 
 d2_unittests_SOURCES = ../d2_log.h ../d2_log.cc
-d2_unittests_SOURCES += ../d_process.h 
+d2_unittests_SOURCES += ../d_process.h
+d2_unittests_SOURCES += ../d_controller.cc ../d2_controller.h
 d2_unittests_SOURCES += ../d2_process.cc ../d2_process.h
+d2_unittests_SOURCES += ../d2_controller.cc ../d2_controller.h
+d2_unittests_SOURCES += d_test_stubs.cc d_test_stubs.h
 d2_unittests_SOURCES += d2_unittests.cc
 d2_unittests_SOURCES += d2_process_unittests.cc
+d2_unittests_SOURCES += d_controller_unittests.cc
+d2_unittests_SOURCES += d2_controller_unittests.cc
 nodist_d2_unittests_SOURCES = ../d2_messages.h ../d2_messages.cc
 
 d2_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)

+ 216 - 0
src/bin/d2/tests/d2_controller_unittests.cc

@@ -0,0 +1,216 @@
+// 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 <config/ccsession.h>
+#include <d_test_stubs.h>
+#include <d2/d2_controller.h>
+#include <d2/spec_config.h>
+
+#include <boost/pointer_cast.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <gtest/gtest.h>
+
+#include <config.h>
+#include <sstream>
+
+using namespace boost::posix_time;
+
+namespace isc {
+namespace d2 {
+
+/// @brief Test fixture class for testing D2Controller class. This class
+/// derives from DControllerTest and wraps a D2Controller.  Much of the
+/// underlying functionality is in the DControllerBase class which has an
+/// extensive set of unit tests that are independent of DHCP-DDNS.
+/// @TODO Currently These tests are relatively light and duplicate some of
+/// the testing done on the base class.  These tests are sufficient to ensure
+/// that D2Controller properly derives from its base class and to test the
+/// logic that is unique to D2Controller. These tests will be augmented and
+/// or new tests added as additional functionality evolves.
+/// Unlike the stub testing, there is no use of SimFailure to induce error
+/// conditions as this is production code.
+class D2ControllerTest : public DControllerTest {
+public:
+    /// @brief Constructor
+    /// Note the constructor passes in the static D2Controller instance
+    /// method.
+    D2ControllerTest() : DControllerTest(D2Controller::instance) {
+    }
+
+    /// @brief Destructor
+    ~D2ControllerTest() {
+    }
+};
+
+/// @brief Basic Controller instantiation testing.
+/// Verfies that the controller singleton gets created and that the
+/// basic derivation from the base class is intact.
+TEST_F(D2ControllerTest, basicInstanceTesting) {
+    // Verify the we can the singleton instance can be fetched and that
+    // it is the correct type.
+    DControllerBasePtr& controller = DControllerTest::getController();
+    ASSERT_TRUE(controller);
+    ASSERT_NO_THROW(boost::dynamic_pointer_cast<D2Controller>(controller));
+
+    // Verify that controller's name is correct.
+    EXPECT_TRUE(checkName(D2_MODULE_NAME));
+
+    // Verify that controller's spec file name is correct.
+    EXPECT_TRUE(checkSpecFileName(D2_SPECFILE_LOCATION));
+
+    // Verify that controller's IOService exists.
+    EXPECT_TRUE(checkIOService());
+
+    // Verify that the Process does NOT exist.
+    EXPECT_FALSE(checkProcess());
+}
+
+/// @brief Tests basic command line processing.
+/// Verfies that:
+/// 1. Standard command line options are supported.
+/// 2. Invalid options are detected.
+TEST_F(D2ControllerTest, commandLineArgs) {
+    char* argv[] = { const_cast<char*>("progName"), 
+                     const_cast<char*>("-s"), 
+                     const_cast<char*>("-v") };
+    int argc = 3;
+
+    // Verify that both flags are false initially.
+    EXPECT_TRUE(checkStandAlone(false));
+    EXPECT_TRUE(checkVerbose(false));
+
+    // Verify that standard options can be parsed without error.
+    EXPECT_NO_THROW(parseArgs(argc, argv));
+
+    // Verify that flags are now true.
+    EXPECT_TRUE(checkStandAlone(true));
+    EXPECT_TRUE(checkVerbose(true));
+
+    // Verify that an unknown option is detected.
+    char* argv2[] = { const_cast<char*>("progName"), 
+                      const_cast<char*>("-x") };
+    argc = 2;
+    EXPECT_THROW(parseArgs(argc, argv2), InvalidUsage);
+}
+
+/// @brief Tests application process creation and initialization.
+/// Verifies that the process can be successfully created and initialized.
+TEST_F(D2ControllerTest, initProcessTesting) {
+    ASSERT_NO_THROW(initProcess());
+    EXPECT_TRUE(checkProcess());
+}
+
+/// @brief Tests launch and normal shutdown (stand alone mode).
+/// This creates an interval timer to generate a normal shutdown and then
+/// launches with a valid, stand-alone command line and no simulated errors.
+TEST_F(D2ControllerTest, launchNormalShutdown) {
+    // command line to run standalone
+    char* argv[] = { const_cast<char*>("progName"), 
+                     const_cast<char*>("-s"), 
+                     const_cast<char*>("-v") };
+    int argc = 3;
+
+    // Use an asiolink IntervalTimer and callback to generate the
+    // shutdown invocation. (Note IntervalTimer setup is in milliseconds).
+    isc::asiolink::IntervalTimer timer(*getIOService());
+    timer.setup(genShutdownCallback, 2 * 1000);
+
+    // Record start time, and invoke launch().
+    ptime start = microsec_clock::universal_time();
+    EXPECT_NO_THROW(launch(argc, argv));
+
+    // Record stop time.
+    ptime stop = microsec_clock::universal_time();
+
+    // Verify that duration of the run invocation is the same as the
+    // timer duration.  This demonstrates that the shutdown was driven
+    // by an io_service event and callback.
+    time_duration elapsed = stop - start;
+    EXPECT_TRUE(elapsed.total_milliseconds() >= 1900 &&
+                elapsed.total_milliseconds() <= 2100);
+}
+
+/// @brief Configuration update event testing.
+/// This really tests just the ability of the handlers to invoke the necessary
+/// chain of methods and handle error conditions. Configuration parsing and
+/// retrieval should be tested as part of the d2 configuration management
+/// implementation.  Note that this testing calls the configuration update event
+/// callback, configHandler, directly.
+/// This test verifies that:
+/// 1. Configuration will be rejected in integrated mode when there is no
+/// session established. (This is a very contrived situation).
+/// 2. In stand-alone mode a configuration update results in successful
+/// status return.
+/// 3. That an application process error in configuration updating is handled
+/// properly.
+TEST_F(D2ControllerTest, configUpdateTests) {
+    int rcode = -1;
+    isc::data::ConstElementPtr answer;
+
+    // Initialize the application process.
+    ASSERT_NO_THROW(initProcess());
+    EXPECT_TRUE(checkProcess());
+
+    // Create a configuration set. Content is arbitrary, just needs to be
+    // valid JSON.
+    std::string config = "{ \"test-value\": 1000 } ";
+    isc::data::ElementPtr config_set = isc::data::Element::fromJSON(config);
+
+    // We are not stand-alone, so configuration should be rejected as there is
+    // no session.  This is a pretty contrived situation that shouldn't be
+    // possible other than the handler being called directly (like this does).
+    answer = DControllerBase::configHandler(config_set);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(1, rcode);
+
+    // Verify that in stand alone we get a successful update result.
+    setStandAlone(true);
+    answer = DControllerBase::configHandler(config_set);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(0, rcode);
+}
+
+/// @brief Command execution tests.
+/// This really tests just the ability of the handler to invoke the necessary
+/// chain of methods and to handle error conditions. Note that this testing
+/// calls the command callback, commandHandler, directly.
+/// This test verifies that:
+/// 1. That an unrecognized command is detected and returns a status of
+/// d2::COMMAND_INVALID.
+/// 2. Shutdown command is recognized and returns a d2::COMMAND_SUCCESS status.
+TEST_F(D2ControllerTest, executeCommandTests) {
+    int rcode = -1;
+    isc::data::ConstElementPtr answer;
+    isc::data::ElementPtr arg_set;
+
+    // Initialize the application process.
+    ASSERT_NO_THROW(initProcess());
+    EXPECT_TRUE(checkProcess());
+
+    // Verify that an unknown command returns an COMMAND_INVALID response.
+    std::string bogus_command("bogus");
+    answer = DControllerBase::commandHandler(bogus_command, arg_set);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(COMMAND_INVALID, rcode);
+
+    // Verify that shutdown command returns COMMAND_SUCCESS response.
+    //answer = executeCommand(SHUT_DOWN_COMMAND, isc::data::ElementPtr());
+    answer = DControllerBase::commandHandler(SHUT_DOWN_COMMAND, arg_set);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(COMMAND_SUCCESS, rcode);
+
+}
+
+}; // end of isc::d2 namespace
+}; // end of isc namespace

+ 21 - 27
src/bin/d2/tests/d2_process_unittests.cc

@@ -43,7 +43,7 @@ public:
         process_.reset(new D2Process("TestProcess", io_service_));
     }
 
-    /// @brief Destructor 
+    /// @brief Destructor
     ~D2ProcessTest() {
         io_service_.reset();
         process_.reset();
@@ -59,7 +59,7 @@ public:
         isc_throw (DProcessBaseError, "simulated fatal error");
     }
 
-    /// @brief IOService for event processing. Fills in for IOservice
+    /// @brief IOService for event processing. Fills in for IOService
     /// supplied by management layer.
     IOServicePtr io_service_;
 };
@@ -83,36 +83,36 @@ TEST(D2Process, construction) {
 }
 
 /// @brief Verifies basic configure method behavior.
-// @TODO This test is simplistic and will need to be augmented
-// as configuration ability is implemented.
+/// @TODO This test is simplistic and will need to be augmented as configuration
+/// ability is implemented.
 TEST_F(D2ProcessTest, configure) {
     // Verify that given a configuration "set", configure returns
     // a successful response.
     int rcode = -1;
     string config = "{ \"test-value\": 1000 } ";
     isc::data::ElementPtr json = isc::data::Element::fromJSON(config);
-    isc::data::ConstElementPtr answer = process_->configure(json); 
+    isc::data::ConstElementPtr answer = process_->configure(json);
     isc::config::parseAnswer(rcode, answer);
     EXPECT_EQ(0, rcode);
 }
 
-/// @brief Verifies basic command method behavior. 
-// @TODO IF the D2Process is extended to support extra commands
-// this testing will need to augmented accordingly.
+/// @brief Verifies basic command method behavior.
+/// @TODO IF the D2Process is extended to support extra commands this testing
+/// will need to augmented accordingly.
 TEST_F(D2ProcessTest, command) {
-    // Verfiy that the process will process unsupported command and
+    // Verify that the process will process unsupported command and
     // return a failure response.
     int rcode = -1;
     string args = "{ \"arg1\": 77 } ";
     isc::data::ElementPtr json = isc::data::Element::fromJSON(args);
-    isc::data::ConstElementPtr answer = 
-                                    process_->command("bogus_command", json); 
+    isc::data::ConstElementPtr answer =
+                                    process_->command("bogus_command", json);
     parseAnswer(rcode, answer);
-    EXPECT_EQ(1, rcode);
+    EXPECT_EQ(COMMAND_INVALID, rcode);
 }
 
-/// @brief Verifies that an "external" call to shutdown causes 
-/// the run method to exit gracefully with a return value of EXIT_SUCCESS.
+/// @brief Verifies that an "external" call to shutdown causes the run method
+/// to exit gracefully.
 TEST_F(D2ProcessTest, normalShutdown) {
     // Use an asiolink IntervalTimer and callback to generate the
     // shutdown invocation. (Note IntervalTimer setup is in milliseconds).
@@ -121,24 +121,21 @@ TEST_F(D2ProcessTest, normalShutdown) {
 
     // Record start time, and invoke run().
     ptime start = microsec_clock::universal_time();
-    int rcode = process_->run();
+    EXPECT_NO_THROW(process_->run());
 
     // Record stop time.
     ptime stop = microsec_clock::universal_time();
 
-    // Verify normal shutdown status.
-    EXPECT_EQ(EXIT_SUCCESS, rcode);
-
     // Verify that duration of the run invocation is the same as the
     // timer duration.  This demonstrates that the shutdown was driven
     // by an io_service event and callback.
     time_duration elapsed = stop - start;
-    EXPECT_TRUE(elapsed.total_milliseconds() >= 1900 && 
+    EXPECT_TRUE(elapsed.total_milliseconds() >= 1900 &&
                 elapsed.total_milliseconds() <= 2100);
 }
 
-/// @brief Verifies that an "uncaught" exception thrown during event loop 
-/// processing is treated as a fatal error.
+/// @brief Verifies that an "uncaught" exception thrown during event loop
+/// execution is treated as a fatal error.
 TEST_F(D2ProcessTest, fatalErrorShutdown) {
     // Use an asiolink IntervalTimer and callback to generate the
     // the exception.  (Note IntervalTimer setup is in milliseconds).
@@ -147,19 +144,16 @@ TEST_F(D2ProcessTest, fatalErrorShutdown) {
 
     // Record start time, and invoke run().
     ptime start = microsec_clock::universal_time();
-    int rcode = process_->run();
+    EXPECT_THROW(process_->run(), DProcessBaseError);
 
     // Record stop time.
     ptime stop = microsec_clock::universal_time();
 
-    // Verify failure status.
-    EXPECT_EQ(EXIT_FAILURE, rcode);
-
     // Verify that duration of the run invocation is the same as the
     // timer duration.  This demonstrates that the anomaly occurred
-    // during io callback processing. 
+    // during io callback processing.
     time_duration elapsed = stop - start;
-    EXPECT_TRUE(elapsed.total_milliseconds() >= 1900 && 
+    EXPECT_TRUE(elapsed.total_milliseconds() >= 1900 &&
                 elapsed.total_milliseconds() <= 2100);
 }
 

+ 2 - 1
src/bin/d2/tests/d2_test.py

@@ -159,7 +159,8 @@ class TestD2Daemon(unittest.TestCase):
         print("Note: Simple test to verify that D2 server can be started.")
         # note that "-s" for stand alone is necessary in order to flush the log output
         # soon enough to catch it.
-        (returncode, output, error) = self.runCommand(["../b10-d2", "-s"])
+        (returncode, output, error) = self.runCommand(["../b10-dhcp-ddns", 
+                                                       "-s", "-v"])
         output_text = str(output) + str(error)
         self.assertEqual(output_text.count("D2CTL_STARTING"), 1)
 

+ 361 - 0
src/bin/d2/tests/d_controller_unittests.cc

@@ -0,0 +1,361 @@
+// 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 <config/ccsession.h>
+#include <d_test_stubs.h>
+#include <d2/spec_config.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <gtest/gtest.h>
+
+#include <config.h>
+#include <sstream>
+
+using namespace boost::posix_time;
+
+namespace isc {
+namespace d2 {
+
+/// @brief Test fixture class for testing DControllerBase class. This class
+/// derives from DControllerTest and wraps a DStubController.  DStubController
+/// has been constructed to exercise DControllerBase.
+class DStubControllerTest : public DControllerTest {
+public:
+
+    /// @brief Constructor.
+    /// Note the constructor passes in the static DStubController instance
+    /// method.
+    DStubControllerTest() : DControllerTest (DStubController::instance) {
+    }
+
+    virtual ~DStubControllerTest() {
+    }
+};
+
+/// @brief Basic Controller instantiation testing.
+/// Verfies that the controller singleton gets created and that the
+/// basic derivation from the base class is intact.
+TEST_F(DStubControllerTest, basicInstanceTesting) {
+    // Verify that the singleton exists and it is the correct type.
+    DControllerBasePtr& controller = DControllerTest::getController();
+    ASSERT_TRUE(controller);
+    ASSERT_NO_THROW(boost::dynamic_pointer_cast<DStubController>(controller));
+
+    // Verify that controller's name is correct.
+    EXPECT_TRUE(checkName(D2_MODULE_NAME));
+
+    // Verify that controller's spec file name is correct.
+    EXPECT_TRUE(checkSpecFileName(D2_SPECFILE_LOCATION));
+
+    // Verify that controller's IOService exists.
+    EXPECT_TRUE(checkIOService());
+
+    // Verify that the Process does NOT exist.
+    EXPECT_FALSE(checkProcess());
+}
+
+/// @brief Tests basic command line processing.
+/// Verifies that:
+/// 1. Standard command line options are supported.
+/// 2. Custom command line options are supported.
+/// 3. Invalid options are detected.
+/// 4. Extraneous command line information is detected.
+TEST_F(DStubControllerTest, commandLineArgs) {
+
+    // Verify that both flags are false initially.
+    EXPECT_TRUE(checkStandAlone(false));
+    EXPECT_TRUE(checkVerbose(false));
+
+    // Verify that standard options can be parsed without error.
+    char* argv[] = { const_cast<char*>("progName"),
+                     const_cast<char*>("-s"),
+                     const_cast<char*>("-v") };
+    int argc = 3;
+    EXPECT_NO_THROW(parseArgs(argc, argv));
+
+    // Verify that flags are now true.
+    EXPECT_TRUE(checkStandAlone(true));
+    EXPECT_TRUE(checkVerbose(true));
+
+    // Verify that the custom command line option is parsed without error.
+    char xopt[3] = "- ";
+    xopt[1] =  *DStubController::stub_option_x_;
+    char* argv1[] = { const_cast<char*>("progName"), xopt};
+    argc = 2;
+    EXPECT_NO_THROW (parseArgs(argc, argv1));
+
+    // Verify that an unknown option is detected.
+    char* argv2[] = { const_cast<char*>("progName"),
+                      const_cast<char*>("-bs") };
+    argc = 2;
+    EXPECT_THROW (parseArgs(argc, argv2), InvalidUsage);
+
+    // Verify that extraneous information is detected.
+    char* argv3[] = { const_cast<char*>("progName"),
+                      const_cast<char*>("extra"),
+                      const_cast<char*>("information") };
+    argc = 3;
+    EXPECT_THROW (parseArgs(argc, argv3), InvalidUsage);
+}
+
+/// @brief Tests application process creation and initialization.
+/// Verifies that:
+/// 1. An error during process creation is handled.
+/// 2. A NULL returned by process creation is handled.
+/// 3. An error during process initialization is handled.
+/// 4. Process can be successfully created and initialized.
+TEST_F(DStubControllerTest, initProcessTesting) {
+    // Verify that a failure during process creation is caught.
+    SimFailure::set(SimFailure::ftCreateProcessException);
+    EXPECT_THROW(initProcess(), DControllerBaseError);
+    EXPECT_FALSE(checkProcess());
+
+    // Verify that a NULL returned by process creation is handled.
+    SimFailure::set(SimFailure::ftCreateProcessNull);
+    EXPECT_THROW(initProcess(), DControllerBaseError);
+    EXPECT_FALSE(checkProcess());
+
+    // Re-create controller, verify that we are starting clean
+    resetController();
+    EXPECT_FALSE(checkProcess());
+
+    // Verify that an error during process initialization is handled.
+    SimFailure::set(SimFailure::ftProcessInit);
+    EXPECT_THROW(initProcess(), DProcessBaseError);
+
+    // Re-create controller, verify that we are starting clean
+    resetController();
+    EXPECT_FALSE(checkProcess());
+
+    // Verify that the application process can created and initialized.
+    ASSERT_NO_THROW(initProcess());
+    EXPECT_TRUE(checkProcess());
+}
+
+/// @brief Tests launch handling of invalid command line.
+/// This test launches with an invalid command line which should throw
+/// an InvalidUsage.
+TEST_F(DStubControllerTest, launchInvalidUsage) {
+    // Command line to run integrated
+    char* argv[] = { const_cast<char*>("progName"),
+                     const_cast<char*>("-z") };
+    int argc = 2;
+
+    // Launch the controller in integrated mode.
+    EXPECT_THROW(launch(argc, argv), InvalidUsage);
+}
+
+/// @brief Tests launch handling of failure in application process
+/// initialization.  This test launches with a valid command line but with
+/// SimFailure set to fail during process creation.  Launch should throw
+/// ProcessInitError.
+TEST_F(DStubControllerTest, launchProcessInitError) {
+    // Command line to run integrated
+    char* argv[] = { const_cast<char*>("progName"),
+                     const_cast<char*>("-s"),
+                     const_cast<char*>("-v") };
+    int argc = 3;
+
+    // Launch the controller in stand alone mode.
+    SimFailure::set(SimFailure::ftCreateProcessException);
+    EXPECT_THROW(launch(argc, argv), ProcessInitError);
+}
+
+/// @brief Tests launch and normal shutdown (stand alone mode).
+/// This creates an interval timer to generate a normal shutdown and then
+/// launches with a valid, stand-alone command line and no simulated errors.
+TEST_F(DStubControllerTest, launchNormalShutdown) {
+    // command line to run standalone
+    char* argv[] = { const_cast<char*>("progName"),
+                     const_cast<char*>("-s"),
+                     const_cast<char*>("-v") };
+    int argc = 3;
+
+    // Use an asiolink IntervalTimer and callback to generate the
+    // shutdown invocation. (Note IntervalTimer setup is in milliseconds).
+    isc::asiolink::IntervalTimer timer(*getIOService());
+    timer.setup(genShutdownCallback, 2 * 1000);
+
+    // Record start time, and invoke launch().
+    ptime start = microsec_clock::universal_time();
+    EXPECT_NO_THROW(launch(argc, argv));
+
+    // Record stop time.
+    ptime stop = microsec_clock::universal_time();
+
+    // Verify that duration of the run invocation is the same as the
+    // timer duration.  This demonstrates that the shutdown was driven
+    // by an io_service event and callback.
+    time_duration elapsed = stop - start;
+    EXPECT_TRUE(elapsed.total_milliseconds() >= 1900 &&
+                elapsed.total_milliseconds() <= 2100);
+}
+
+/// @brief Tests launch with an operational error during application execution.
+/// This test creates an interval timer to generate a runtime exception during
+/// the process event loop. It launches wih a valid, stand-alone command line
+/// and no simulated errors.  Launch should throw ProcessRunError.
+TEST_F(DStubControllerTest, launchRuntimeError) {
+    // command line to run standalone
+    char* argv[] = { const_cast<char*>("progName"),
+                     const_cast<char*>("-s"),
+                     const_cast<char*>("-v") };
+    int argc = 3;
+
+    // Use an asiolink IntervalTimer and callback to generate the
+    // shutdown invocation. (Note IntervalTimer setup is in milliseconds).
+    isc::asiolink::IntervalTimer timer(*getIOService());
+    timer.setup(genFatalErrorCallback, 2 * 1000);
+
+    // Record start time, and invoke launch().
+    ptime start = microsec_clock::universal_time();
+    EXPECT_THROW(launch(argc, argv), ProcessRunError);
+
+    // Record stop time.
+    ptime stop = microsec_clock::universal_time();
+
+    // Verify that duration of the run invocation is the same as the
+    // timer duration.  This demonstrates that the shutdown was driven
+    // by an io_service event and callback.
+    time_duration elapsed = stop - start;
+    EXPECT_TRUE(elapsed.total_milliseconds() >= 1900 &&
+                elapsed.total_milliseconds() <= 2100);
+}
+
+/// @brief Tests launch with a session establishment failure.
+/// This test launches with a valid command line for integrated mode and no.
+/// Attempting to connect to BIND10 should fail, even if BIND10 is running
+/// UNLESS the test is run as root.  Launch should throw SessionStartError.
+TEST_F(DStubControllerTest, launchSessionFailure) {
+    // Command line to run integrated
+    char* argv[] = { (char*)"progName" };
+    int argc = 1;
+
+    // Launch the controller in integrated mode.
+    EXPECT_THROW(launch(argc, argv), SessionStartError);
+}
+
+/// @brief Configuration update event testing.
+/// This really tests just the ability of the handlers to invoke the necessary
+/// chain of methods and handle error conditions. Configuration parsing and
+/// retrieval should be tested as part of the d2 configuration management
+/// implementation.  Note that this testing calls the configuration update event
+/// callback, configHandler, directly.
+/// This test verifies that:
+/// 1. Configuration will be rejected in integrated mode when there is no
+/// session established. (This is a very contrived situation).
+/// 2. In stand-alone mode a configuration update results in successful
+/// status return.
+/// 3. That an application process error in configuration updating is handled
+/// properly.
+TEST_F(DStubControllerTest, configUpdateTests) {
+    int rcode = -1;
+    isc::data::ConstElementPtr answer;
+
+    // Initialize the application process.
+    ASSERT_NO_THROW(initProcess());
+    EXPECT_TRUE(checkProcess());
+
+    // Create a configuration set. Content is arbitrary, just needs to be
+    // valid JSON.
+    std::string config = "{ \"test-value\": 1000 } ";
+    isc::data::ElementPtr config_set = isc::data::Element::fromJSON(config);
+
+    // We are not stand-alone, so configuration should be rejected as there is
+    // no session.  This is a pretty contrived situation that shouldn't be
+    // possible other than the handler being called directly (like this does).
+    answer = DControllerBase::configHandler(config_set);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(1, rcode);
+
+    // Verify that in stand alone we get a successful update result.
+    setStandAlone(true);
+    answer = DControllerBase::configHandler(config_set);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(0, rcode);
+
+    // Verify that an error in process configure method is handled.
+    SimFailure::set(SimFailure::ftProcessConfigure);
+    answer = DControllerBase::configHandler(config_set);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(1, rcode);
+}
+
+/// @brief Command execution tests.
+/// This really tests just the ability of the handler to invoke the necessary
+/// chain of methods and to handle error conditions. Note that this testing
+/// calls the command callback, commandHandler, directly.
+/// This test verifies that:
+/// 1. That an unrecognized command is detected and returns a status of
+/// d2::COMMAND_INVALID.
+/// 2. Shutdown command is recognized and returns a d2::COMMAND_SUCCESS status.
+/// 3. A valid, custom controller command is recognized a d2::COMMAND_SUCCESS
+/// status.
+/// 4. A valid, custom process command is recognized a d2::COMMAND_SUCCESS
+/// status.
+/// 5. That a valid controller command that fails returns a d2::COMMAND_ERROR.
+/// 6. That a valid process command that fails returns a d2::COMMAND_ERROR.
+TEST_F(DStubControllerTest, executeCommandTests) {
+    int rcode = -1;
+    isc::data::ConstElementPtr answer;
+    isc::data::ElementPtr arg_set;
+
+    // Initialize the application process.
+    ASSERT_NO_THROW(initProcess());
+    EXPECT_TRUE(checkProcess());
+
+    // Verify that an unknown command returns an d2::COMMAND_INVALID response.
+    std::string bogus_command("bogus");
+    answer = DControllerBase::commandHandler(bogus_command, arg_set);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(COMMAND_INVALID, rcode);
+
+    // Verify that shutdown command returns d2::COMMAND_SUCCESS response.
+    answer = DControllerBase::commandHandler(SHUT_DOWN_COMMAND, arg_set);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(COMMAND_SUCCESS, rcode);
+
+    // Verify that a valid custom controller command returns
+    // d2::COMMAND_SUCCESS response.
+    answer = DControllerBase::commandHandler(DStubController::
+                                             stub_ctl_command_, arg_set);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(COMMAND_SUCCESS, rcode);
+
+    // Verify that a valid custom process command returns d2::COMMAND_SUCCESS
+    // response.
+    answer = DControllerBase::commandHandler(DStubProcess::
+                                             stub_proc_command_, arg_set);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(COMMAND_SUCCESS, rcode);
+
+    // Verify that a valid custom controller command that fails returns
+    // a d2::COMMAND_ERROR.
+    SimFailure::set(SimFailure::ftControllerCommand);
+    answer = DControllerBase::commandHandler(DStubController::
+                                             stub_ctl_command_, arg_set);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(COMMAND_ERROR, rcode);
+
+    // Verify that a valid custom process command that fails returns
+    // a d2::COMMAND_ERROR.
+    SimFailure::set(SimFailure::ftProcessCommand);
+    answer = DControllerBase::commandHandler(DStubProcess::
+                                             stub_proc_command_, arg_set);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(COMMAND_ERROR, rcode);
+}
+
+}; // end of isc::d2 namespace
+}; // end of isc namespace

+ 202 - 0
src/bin/d2/tests/d_test_stubs.cc

@@ -0,0 +1,202 @@
+// 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 <d2/d2_log.h>
+#include <d2/spec_config.h>
+#include <d2/tests/d_test_stubs.h>
+
+using namespace asio;
+
+namespace isc {
+namespace d2 {
+
+// Initialize the static failure flag.
+SimFailure::FailureType SimFailure::failure_type_ = SimFailure::ftNoFailure;
+
+// Define custom process command supported by DStubProcess.
+const char*  DStubProcess::stub_proc_command_("cool_proc_cmd");
+
+DStubProcess::DStubProcess(const char* name, IOServicePtr io_service)
+    : DProcessBase(name, io_service) {
+};
+
+void
+DStubProcess::init() {
+    LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2PRC_PROCESS_INIT);
+    if (SimFailure::shouldFailOn(SimFailure::ftProcessInit)) {
+        // Simulates a failure to instantiate the process.
+        isc_throw(DProcessBaseError, "DStubProcess simulated init() failure");
+    }
+};
+
+void
+DStubProcess::run() {
+    // Until shut down or an fatal error occurs, wait for and
+    // execute a single callback. This is a preliminary implementation
+    // that is likely to evolve as development progresses.
+    // To use run(), the "managing" layer must issue an io_service::stop
+    // or the call to run will continue to block, and shutdown will not
+    // occur.
+    LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2PRC_RUN_ENTER);
+    IOServicePtr& io_service = getIoService();
+    while (!shouldShutdown()) {
+        try {
+            io_service->run_one();
+        } catch (const std::exception& ex) {
+            LOG_FATAL(d2_logger, D2PRC_FAILED).arg(ex.what());
+            isc_throw (DProcessBaseError,
+                std::string("Process run method failed:") + ex.what());
+        }
+    }
+
+    LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2PRC_RUN_EXIT);
+};
+
+void
+DStubProcess::shutdown() {
+    LOG_DEBUG(d2_logger, DBGLVL_START_SHUT, D2PRC_SHUTDOWN);
+    if (SimFailure::shouldFailOn(SimFailure::ftProcessShutdown)) {
+        // Simulates a failure during shutdown process.
+        isc_throw(DProcessBaseError, "DStubProcess simulated shutdown failure");
+    }
+
+    DProcessBase::shutdown();
+}
+
+isc::data::ConstElementPtr
+DStubProcess::configure(isc::data::ConstElementPtr config_set) {
+    LOG_DEBUG(d2_logger, DBGLVL_TRACE_BASIC,
+              D2PRC_CONFIGURE).arg(config_set->str());
+
+    if (SimFailure::shouldFailOn(SimFailure::ftProcessConfigure)) {
+        // Simulates a process configure failure.
+        return (isc::config::createAnswer(1,
+                "Simulated process configuration error."));
+    }
+
+    return (isc::config::createAnswer(0, "Configuration accepted."));
+}
+
+isc::data::ConstElementPtr
+DStubProcess::command(const std::string& command,
+                      isc::data::ConstElementPtr args) {
+    LOG_DEBUG(d2_logger, DBGLVL_TRACE_BASIC,
+              D2PRC_COMMAND).arg(command).arg(args->str());
+
+    isc::data::ConstElementPtr answer;
+    if (SimFailure::shouldFailOn(SimFailure::ftProcessCommand)) {
+        // Simulates a process command execution failure.
+        answer = isc::config::createAnswer(COMMAND_ERROR,
+                                          "SimFailure::ftProcessCommand");
+    } else if (command.compare(stub_proc_command_) == 0) {
+        answer = isc::config::createAnswer(COMMAND_SUCCESS, "Command accepted");
+    } else {
+        answer = isc::config::createAnswer(COMMAND_INVALID,
+                                           "Unrecognized command:" + command);
+    }
+
+    return (answer);
+}
+
+DStubProcess::~DStubProcess() {
+};
+
+//************************** DStubController *************************
+
+// Define custom controller command supported by DStubController.
+const char* DStubController::stub_ctl_command_("spiffy");
+
+// Define custom command line option command supported by DStubController.
+const char* DStubController::stub_option_x_ = "x";
+
+DControllerBasePtr&
+DStubController::instance() {
+    // If the singleton hasn't been created, do it now.
+    if (!getController()) {
+        //setController(new DStubController());
+        DControllerBasePtr p(new DStubController());
+        setController(p);
+    }
+
+    return (getController());
+}
+
+DStubController::DStubController()
+    : DControllerBase(D2_MODULE_NAME) {
+
+    if (getenv("B10_FROM_BUILD")) {
+        setSpecFileName(std::string(getenv("B10_FROM_BUILD")) +
+            "/src/bin/d2/d2.spec");
+    } else {
+        setSpecFileName(D2_SPECFILE_LOCATION);
+    }
+}
+
+bool
+DStubController::customOption(int option, char* /* optarg */)
+{
+    // Check for the custom option supported by DStubController.
+    if (static_cast<char>(option) == *stub_option_x_) {
+        return (true);
+    }
+
+    return (false);
+}
+
+DProcessBase* DStubController::createProcess() {
+    if (SimFailure::shouldFailOn(SimFailure::ftCreateProcessException)) {
+        // Simulates a failure to instantiate the process due to exception.
+        throw std::runtime_error("SimFailure::ftCreateProcess");
+    }
+
+    if (SimFailure::shouldFailOn(SimFailure::ftCreateProcessNull)) {
+        // Simulates a failure to instantiate the process.
+        return (NULL);
+    }
+
+    // This should be a successful instantiation.
+    return (new DStubProcess(getName().c_str(), getIOService()));
+}
+
+isc::data::ConstElementPtr
+DStubController::customControllerCommand(const std::string& command,
+                                     isc::data::ConstElementPtr /* args */) {
+    isc::data::ConstElementPtr answer;
+    if (SimFailure::shouldFailOn(SimFailure::ftControllerCommand)) {
+        // Simulates command failing to execute.
+        answer = isc::config::createAnswer(COMMAND_ERROR,
+                                          "SimFailure::ftControllerCommand");
+    } else if (command.compare(stub_ctl_command_) == 0) {
+        answer = isc::config::createAnswer(COMMAND_SUCCESS, "Command accepted");
+    } else {
+        answer = isc::config::createAnswer(COMMAND_INVALID,
+                                           "Unrecognized command:" + command);
+    }
+
+    return (answer);
+}
+
+const std::string DStubController::getCustomOpts() const {
+    // Return the "list" of custom options supported by DStubController.
+    return (std::string(stub_option_x_));
+}
+
+DStubController::~DStubController() {
+}
+
+// Initialize controller wrapper's static instance getter member.
+DControllerTest::InstanceGetter DControllerTest::instanceGetter_ = NULL;
+
+}; // namespace isc::d2
+}; // namespace isc

+ 422 - 0
src/bin/d2/tests/d_test_stubs.h

@@ -0,0 +1,422 @@
+// 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 D_TEST_STUBS_H
+#define D_TEST_STUBS_H
+
+#include <asiolink/asiolink.h>
+#include <cc/data.h>
+#include <cc/session.h>
+#include <config/ccsession.h>
+
+#include <d2/d_controller.h>
+#include <gtest/gtest.h>
+
+namespace isc {
+namespace d2 {
+
+/// @brief Class is used to set a globally accessible value that indicates
+/// a specific type of failure to simulate.  Test derivations of base classes
+/// can exercise error handling code paths by testing for specific SimFailure
+/// values at the appropriate places and then causing the error to "occur".
+/// The class consists of an enumerated set of failures, and static methods
+/// for getting, setting, and testing the current value.
+class SimFailure {
+public:
+    enum FailureType {
+        ftUnknown = -1,
+        ftNoFailure = 0,
+        ftCreateProcessException,
+        ftCreateProcessNull,
+        ftProcessInit,
+        ftProcessConfigure,
+        ftControllerCommand,
+        ftProcessCommand,
+        ftProcessShutdown
+    };
+
+    /// @brief Sets the SimFailure value to the given value.
+    ///
+    /// @param value is the new value to assign to the global value.
+    static void set(enum FailureType value) {
+       failure_type_ = value;
+    }
+
+    /// @brief Gets the current global SimFailure value
+    ///
+    /// @return returns the current SimFailure value
+    static enum FailureType get() {
+       return (failure_type_);
+    }
+
+    /// @brief One-shot test of the SimFailure value. If the global
+    /// SimFailure value is equal to the given value, clear the global
+    /// value and return true.  This makes it convenient for code to
+    /// test and react without having to explicitly clear the global
+    /// value.
+    ///
+    /// @param value is the value against which the global value is
+    /// to be compared.
+    ///
+    /// @return returns true if current SimFailure value matches the
+    /// given value.
+    static bool shouldFailOn(enum FailureType value) {
+        if (failure_type_ == value) {
+            clear();
+            return (true);
+        }
+
+        return (false);
+    }
+
+    static void clear() {
+       failure_type_ = ftNoFailure;
+    }
+
+    static enum FailureType failure_type_;
+};
+
+/// @brief Test Derivation of the DProcessBase class.
+///
+/// This class is used primarily to server as a test process class for testing
+/// DControllerBase.  It provides minimal, but sufficient implementation to
+/// test the majority of DControllerBase functionality.
+class DStubProcess : public DProcessBase {
+public:
+
+    /// @brief Static constant that defines a custom process command string.
+    static const char* stub_proc_command_;
+
+    /// @brief Constructor
+    ///
+    /// @param name name is a text label for the process. Generally used
+    /// in log statements, but otherwise arbitrary.
+    /// @param io_service is the io_service used by the caller for
+    /// asynchronous event handling.
+    ///
+    /// @throw DProcessBaseError is io_service is NULL.
+    DStubProcess(const char* name, IOServicePtr io_service);
+
+    /// @brief Invoked after process instantiation to perform initialization.
+    /// This implementation supports simulating an error initializing the
+    /// process by throwing a DProcessBaseError if SimFailure is set to
+    /// ftProcessInit.
+    virtual void init();
+
+    /// @brief Implements the process's event loop.
+    /// This implementation is quite basic, surrounding calls to
+    /// io_service->runOne() with a test of the shutdown flag. Once invoked,
+    /// the method will continue until the process itself is exiting due to a
+    /// request to shutdown or some anomaly forces an exit.
+    /// @return  returns 0 upon a successful, "normal" termination, non-zero to
+    /// indicate an abnormal termination.
+    virtual void run();
+
+    /// @brief Implements the process shutdown procedure. Currently this is
+    /// limited to setting the instance shutdown flag, which is monitored in
+    /// run().
+    virtual void shutdown();
+
+    /// @brief Processes the given configuration.
+    ///
+    /// This implementation fails if SimFailure is set to ftProcessConfigure.
+    /// Otherwise it will complete successfully.  It does not check the content
+    /// of the inbound configuration.
+    ///
+    /// @param config_set a new configuration (JSON) for the process
+    /// @return an Element that contains the results of configuration composed
+    /// of an integer status value (0 means successful, non-zero means failure),
+    /// and a string explanation of the outcome.
+    virtual isc::data::ConstElementPtr configure(isc::data::ConstElementPtr
+                                                 config_set);
+
+    /// @brief Executes the given command.
+    ///
+    /// This implementation will recognizes one "custom" process command,
+    /// stub_proc_command_.  It will fail if SimFailure is set to
+    /// ftProcessCommand.
+    ///
+    /// @param command is a string label representing the command to execute.
+    /// @param args is a set of arguments (if any) required for the given
+    /// command.
+    /// @return an Element that contains the results of command composed
+    /// of an integer status value and a string explanation of the outcome.
+    /// The status value is:
+    /// COMMAND_SUCCESS if the command is recognized and executes successfully.
+    /// COMMAND_ERROR if the command is recognized but fails to execute.
+    /// COMMAND_INVALID if the command is not recognized.
+    virtual isc::data::ConstElementPtr command(const std::string& command,
+                                               isc::data::ConstElementPtr args);
+
+    // @brief Destructor
+    virtual ~DStubProcess();
+};
+
+
+/// @brief Test Derivation of the DControllerBase class.
+///
+/// DControllerBase is an abstract class and therefore requires a derivation
+/// for testing.  It allows testing the majority of the base class code
+/// without polluting production derivations (e.g. D2Process).  It uses
+/// DStubProcess as its application process class.  It is a full enough
+/// implementation to support running both stand alone and integrated.
+/// Obviously BIND10 connectivity is not available under unit tests, so
+/// testing here is limited to "failures" to communicate with BIND10.
+class DStubController : public DControllerBase {
+public:
+    /// @brief Static singleton instance method. This method returns the
+    /// base class singleton instance member.  It instantiates the singleton
+    /// and sets the base class instance member upon first invocation.
+    ///
+    /// @return returns a pointer reference to the singleton instance.
+    static DControllerBasePtr& instance();
+
+    /// @brief Defines a custom controller command string. This is a
+    /// custom command supported by DStubController.
+    static const char* stub_ctl_command_;
+
+    /// @brief Defines a custom command line option supported by
+    /// DStubController.
+    static const char* stub_option_x_;
+
+protected:
+    /// @brief Handles additional command line options that are supported
+    /// by DStubController.  This implementation supports an option "-x".
+    ///
+    /// @param option is the option "character" from the command line, without
+    /// any prefixing hyphen(s)
+    /// @optarg optarg is the argument value (if one) associated with the option
+    ///
+    /// @return returns true if the option is "x", otherwise ti returns false.
+    virtual bool customOption(int option, char *optarg);
+
+    /// @brief Instantiates an instance of DStubProcess.
+    ///
+    /// This implementation will fail if SimFailure is set to
+    /// ftCreateProcessException OR ftCreateProcessNull.
+    ///
+    /// @return returns a pointer to the new process instance (DProcessBase*)
+    /// or NULL if SimFailure is set to ftCreateProcessNull.
+    /// @throw throws std::runtime_error if SimFailure is set to
+    /// ftCreateProcessException.
+    virtual DProcessBase* createProcess();
+
+    /// @brief Executes custom controller commands are supported by
+    /// DStubController. This implementation supports one custom controller
+    /// command, stub_ctl_command_.  It will fail if SimFailure is set
+    /// to ftControllerCommand.
+    ///
+    /// @param command is a string label representing the command to execute.
+    /// @param args is a set of arguments (if any) required for the given
+    /// command.
+    /// @return an Element that contains the results of command composed
+    /// of an integer status value and a string explanation of the outcome.
+    /// The status value is:
+    /// COMMAND_SUCCESS if the command is recognized and executes successfully.
+    /// COMMAND_ERROR if the command is recognized but fails to execute.
+    /// COMMAND_INVALID if the command is not recognized.
+    virtual isc::data::ConstElementPtr customControllerCommand(
+            const std::string& command, isc::data::ConstElementPtr args);
+
+    /// @brief Provides a string of the additional command line options
+    /// supported by DStubController.  DStubController supports one
+    /// addition option, stub_option_x_.
+    ///
+    /// @return returns a string containing the option letters.
+    virtual const std::string getCustomOpts() const;
+
+private:
+    /// @brief Constructor is private to protect singleton integrity.
+    DStubController();
+
+public:
+    virtual ~DStubController();
+};
+
+/// @brief Abstract Test fixture class that wraps a DControllerBase. This class
+/// is a friend class of DControllerBase which allows it access to class
+/// content to facilitate testing.  It provides numerous wrapper methods for
+/// the protected and private methods and member of the base class.
+class DControllerTest : public ::testing::Test {
+public:
+
+    /// @brief Defines a function pointer for controller singleton fetchers.
+    typedef DControllerBasePtr& (*InstanceGetter)();
+
+    /// @brief Static storage of the controller class's singleton fetcher.
+    /// We need this this statically available for callbacks.
+    static InstanceGetter instanceGetter_;
+
+    /// @brief Constructor
+    ///
+    /// @param instance_getter is a function pointer to the static instance
+    /// method of the DControllerBase derivation under test.
+    DControllerTest(InstanceGetter instance_getter) {
+        // Set the static fetcher member, then invoke it via getController.
+        // This ensures the singleton is instantiated.
+        instanceGetter_ = instance_getter;
+        getController();
+    }
+
+    /// @brief Destructor
+    /// Note the controller singleton is destroyed. This is essential to ensure
+    /// a clean start between tests.
+    virtual ~DControllerTest() {
+        getController().reset();
+    }
+
+    /// @brief Convenience method that destructs and then recreates the
+    /// controller singleton under test.  This is handy for tests within
+    /// tests.
+    void resetController() {
+        getController().reset();
+        getController();
+    }
+
+    /// @brief Static method which returns the instance of the controller
+    /// under test.
+    /// @return returns a reference to the controller instance.
+    static DControllerBasePtr& getController() {
+        return ((*instanceGetter_)());
+    }
+
+    /// @brief Returns true if the Controller's name matches the given value.
+    ///
+    /// @param should_be is the value to compare against.
+    ///
+    /// @return returns true if the values are equal.
+    bool checkName(const std::string& should_be) {
+        return (getController()->getName().compare(should_be) == 0);
+    }
+
+    /// @brief Returns true if the Controller's spec file name matches the
+    /// given value.
+    ///
+    /// @param should_be is the value to compare against.
+    ///
+    /// @return returns true if the values are equal.
+    bool checkSpecFileName(const std::string& should_be) {
+        return (getController()->getSpecFileName().compare(should_be) == 0);
+    }
+
+    /// @brief Tests the existence of the Controller's application process.
+    ///
+    /// @return returns true if the process instance exists.
+    bool checkProcess() {
+        return (getController()->process_);
+    }
+
+    /// @brief Tests the existence of the Controller's IOService.
+    ///
+    /// @return returns true if the IOService exists.
+    bool checkIOService() {
+        return (getController()->io_service_);
+    }
+
+    /// @brief Gets the Controller's IOService.
+    ///
+    /// @return returns a reference to the IOService
+    IOServicePtr& getIOService() {
+        return (getController()->io_service_);
+    }
+
+    /// @brief Compares stand alone flag with the given value.
+    ///
+    /// @param value
+    ///
+    /// @return returns true if the stand alone flag is equal to the given
+    /// value.
+    bool checkStandAlone(bool value) {
+        return (getController()->isStandAlone() == value);
+    }
+
+    /// @brief Sets the controller's stand alone flag to the given value.
+    ///
+    /// @param value is the new value to assign.
+    ///
+    void setStandAlone(bool value) {
+        getController()->setStandAlone(value);
+    }
+
+    /// @brief Compares verbose flag with the given value.
+    ///
+    /// @param value
+    ///
+    /// @return returns true if the verbose flag is equal to the given value.
+    bool checkVerbose(bool value) {
+        return (getController()->isVerbose() == value);
+    }
+
+    /// @Wrapper to invoke the Controller's parseArgs method.  Please refer to
+    /// DControllerBase::parseArgs for details.
+    void parseArgs(int argc, char* argv[]) {
+        getController()->parseArgs(argc, argv);
+    }
+
+    /// @Wrapper to invoke the Controller's init method.  Please refer to
+    /// DControllerBase::init for details.
+    void initProcess() {
+        getController()->initProcess();
+    }
+
+    /// @Wrapper to invoke the Controller's establishSession method.  Please
+    /// refer to DControllerBase::establishSession for details.
+    void establishSession() {
+        getController()->establishSession();
+    }
+
+    /// @Wrapper to invoke the Controller's launch method.  Please refer to
+    /// DControllerBase::launch for details.
+    void launch(int argc, char* argv[]) {
+        optind = 1;
+        getController()->launch(argc, argv);
+    }
+
+    /// @Wrapper to invoke the Controller's disconnectSession method.  Please
+    /// refer to DControllerBase::disconnectSession for details.
+    void disconnectSession() {
+        getController()->disconnectSession();
+    }
+
+    /// @Wrapper to invoke the Controller's updateConfig method.  Please
+    /// refer to DControllerBase::updateConfig for details.
+    isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr
+                                            new_config) {
+        return (getController()->updateConfig(new_config));
+    }
+
+    /// @Wrapper to invoke the Controller's executeCommand method.  Please
+    /// refer to DControllerBase::executeCommand for details.
+    isc::data::ConstElementPtr executeCommand(const std::string& command,
+                                       isc::data::ConstElementPtr args){
+        return (getController()->executeCommand(command, args));
+    }
+
+    /// @brief Callback that will generate shutdown command via the
+    /// command callback function.
+    static void genShutdownCallback() {
+        isc::data::ElementPtr arg_set;
+        DControllerBase::commandHandler(SHUT_DOWN_COMMAND, arg_set);
+    }
+
+    /// @brief Callback that throws an exception.
+    static void genFatalErrorCallback() {
+        isc_throw (DProcessBaseError, "simulated fatal error");
+    }
+};
+
+}; // namespace isc::d2
+}; // namespace isc
+
+#endif