face: give ProtocolFactory access to NetworkMonitor

Subclasses of ProtocolFactory can access NetworkMonitor and
addFace callback anytime via member fields. They are supplied
to ProtocolFactory::create from FaceSystem.

This is in preparation for letting ProtocolFactory subclasses
react to fine-grained signals from NetworkMonitor.

refs #4021

Change-Id: I8da116bffc83d1bdeed7fd3b2e12c8872f19177b
diff --git a/daemon/face/ethernet-factory.cpp b/daemon/face/ethernet-factory.cpp
index 8fc9822..362527f 100644
--- a/daemon/face/ethernet-factory.cpp
+++ b/daemon/face/ethernet-factory.cpp
@@ -128,7 +128,7 @@
         auto channel = this->createChannel(netif, time::seconds(idleTimeout));
         if (wantListen && !channel->isListening()) {
           try {
-            channel->listen(context.addFace, nullptr);
+            channel->listen(this->addFace, nullptr);
           }
           catch (const EthernetChannel::Error& e) {
             NFD_LOG_WARN("Cannot listen on " << netif->getName() << ": " << e.what());
@@ -293,7 +293,7 @@
 
       if (face->getId() == face::INVALID_FACEID) {
         // new face: register with forwarding
-        context.addFace(face);
+        this->addFace(face);
       }
       else {
         // existing face: don't destroy
diff --git a/daemon/face/face-system.cpp b/daemon/face/face-system.cpp
index ee44591..7877ed0 100644
--- a/daemon/face/face-system.cpp
+++ b/daemon/face/face-system.cpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
@@ -25,6 +25,7 @@
 
 #include "face-system.hpp"
 #include "protocol-factory.hpp"
+#include "core/global-io.hpp"
 #include "fw/face-table.hpp"
 
 namespace nfd {
@@ -32,15 +33,23 @@
 
 NFD_LOG_INIT("FaceSystem");
 
-FaceSystem::FaceSystem(FaceTable& faceTable)
+FaceSystem::FaceSystem(FaceTable& faceTable, const shared_ptr<ndn::net::NetworkMonitor>& netmon)
   : m_faceTable(faceTable)
 {
+  BOOST_ASSERT(netmon != nullptr);
+
+  auto addFace = bind(&FaceTable::add, &m_faceTable, _1);
   for (const std::string& id : ProtocolFactory::listRegistered()) {
     NFD_LOG_TRACE("creating factory " << id);
-    m_factories[id] = ProtocolFactory::create(id);
+    m_factories[id] = ProtocolFactory::create(id, netmon, addFace);
   }
 }
 
+FaceSystem::FaceSystem(FaceTable& faceTable)
+  : FaceSystem(faceTable, make_shared<ndn::net::NetworkMonitor>(getGlobalIoService()))
+{
+}
+
 FaceSystem::~FaceSystem() = default;
 
 std::set<const ProtocolFactory*>
@@ -78,7 +87,6 @@
 {
   ConfigContext context;
   context.isDryRun = isDryRun;
-  context.addFace = bind(&FaceTable::add, &m_faceTable, _1);
   context.m_netifs = listNetworkInterfaces();
 
   // process sections in protocol factories
diff --git a/daemon/face/face-system.hpp b/daemon/face/face-system.hpp
index d1f38b9..6557c72 100644
--- a/daemon/face/face-system.hpp
+++ b/daemon/face/face-system.hpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
@@ -30,6 +30,9 @@
 #include "core/config-file.hpp"
 #include "core/network-interface.hpp"
 #include "core/network-interface-predicate.hpp"
+#include <ndn-cxx/net/network-address.hpp>
+#include <ndn-cxx/net/network-interface.hpp>
+#include <ndn-cxx/net/network-monitor.hpp>
 
 namespace nfd {
 
@@ -47,8 +50,11 @@
 class FaceSystem : noncopyable
 {
 public:
+  FaceSystem(FaceTable& faceTable, const shared_ptr<ndn::net::NetworkMonitor>& netmon);
+
+  DEPRECATED(
   explicit
-  FaceSystem(FaceTable& faceTable);
+  FaceSystem(FaceTable& faceTable));
 
   ~FaceSystem();
 
@@ -83,17 +89,15 @@
   class ConfigContext : noncopyable
   {
   public:
+    /// \deprecated use NetworkMonitor provided as ProtocolFactory::netmon
     const std::vector<NetworkInterfaceInfo>&
     listNetifs() const
     {
-      ///\todo get netifs from NetworkMonitor
       return m_netifs;
     }
 
   public:
     bool isDryRun;
-    FaceCreatedCallback addFace;
-    ///\todo add NetworkMonitor
 
   private:
     std::vector<NetworkInterfaceInfo> m_netifs;
diff --git a/daemon/face/protocol-factory.cpp b/daemon/face/protocol-factory.cpp
index daa0214..010190d 100644
--- a/daemon/face/protocol-factory.cpp
+++ b/daemon/face/protocol-factory.cpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
@@ -38,11 +38,19 @@
 }
 
 unique_ptr<ProtocolFactory>
-ProtocolFactory::create(const std::string& id)
+ProtocolFactory::create(const std::string& id, shared_ptr<ndn::net::NetworkMonitor> netmon,
+                        const FaceCreatedCallback& addFace)
 {
   Registry& registry = getRegistry();
   auto found = registry.find(id);
-  return found == registry.end() ? nullptr : found->second();
+  if (found == registry.end()) {
+    return nullptr;
+  }
+
+  auto factory = found->second();
+  factory->netmon = std::move(netmon);
+  factory->addFace = addFace;
+  return factory;
 }
 
 std::set<std::string>
diff --git a/daemon/face/protocol-factory.hpp b/daemon/face/protocol-factory.hpp
index b8bba3d..9d6ad3e 100644
--- a/daemon/face/protocol-factory.hpp
+++ b/daemon/face/protocol-factory.hpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
@@ -29,14 +29,14 @@
 #include "channel.hpp"
 #include "face-system.hpp"
 
-#include <ndn-cxx/encoding/nfd-constants.hpp>
 #include <boost/range/adaptor/map.hpp>
 #include <boost/range/algorithm/copy.hpp>
+#include <ndn-cxx/encoding/nfd-constants.hpp>
 
 namespace nfd {
 namespace face {
 
-/** \brief provide support for an underlying protocol
+/** \brief Provides support for an underlying protocol
  *  \sa FaceSystem
  *
  *  A protocol factory provides support for an underlying protocol and owns Channel objects.
@@ -46,7 +46,7 @@
 class ProtocolFactory : noncopyable
 {
 public: // registry
-  /** \brief register a protocol factory type
+  /** \brief Register a protocol factory type
    *  \tparam S subclass of ProtocolFactory
    *  \param id factory identifier
    */
@@ -59,20 +59,21 @@
     registry[id] = &make_unique<PF>;
   }
 
-  /** \return a protocol factory instance
+  /** \brief Create a protocol factory instance
    *  \retval nullptr if factory with \p id is not registered
    */
   static unique_ptr<ProtocolFactory>
-  create(const std::string& id);
+  create(const std::string& id, shared_ptr<ndn::net::NetworkMonitor> netmon,
+         const FaceCreatedCallback& addFace);
 
-  /** \return registered protocol factory ids
+  /** \brief Get registered protocol factory ids
    */
   static std::set<std::string>
   listRegistered();
 
 public:
   /**
-   * \brief Base class for all exceptions thrown by protocol factories
+   * \brief Base class for all exceptions thrown by ProtocolFactory subclasses
    */
   class Error : public std::runtime_error
   {
@@ -88,7 +89,7 @@
   ~ProtocolFactory() = default;
 
 #ifdef DOXYGEN
-  /** \return protocol factory id
+  /** \brief Get id for this ProtocolFactory
    *
    *  face_system.factory-id config section is processed by the protocol factory.
    */
@@ -96,7 +97,7 @@
   getId();
 #endif
 
-  /** \brief process face_system subsection that corresponds to this ProtocolFactory type
+  /** \brief Process face_system subsection that corresponds to this ProtocolFactory type
    *  \param configSection the configuration section or boost::null to indicate it is omitted
    *  \param context provides access to data structures and contextual information
    *  \throw ConfigFile::Error invalid configuration
@@ -107,7 +108,7 @@
   processConfig(OptionalConfigSection configSection,
                 FaceSystem::ConfigContext& context) = 0;
 
-  /** \return FaceUri schemes accepted by this ProtocolFactory
+  /** \brief Get FaceUri schemes accepted by this ProtocolFactory
    */
   const std::set<std::string>&
   getProvidedSchemes()
@@ -154,9 +155,16 @@
   getRegistry();
 
 protected:
-  /** \brief FaceUri schemes provided by this ProtocolFactory
+  std::set<std::string> providedSchemes; ///< FaceUri schemes provided by this ProtocolFactory
+
+  /** \brief NetworkMonitor for listing available network interfaces and monitoring their changes
+   *
+   *  ProtocolFactory subclass should check the NetworkMonitor has sufficient capabilities prior
+   *  to usage.
    */
-  std::set<std::string> providedSchemes;
+  shared_ptr<ndn::net::NetworkMonitor> netmon;
+
+  FaceCreatedCallback addFace; ///< callback when a new face is created
 };
 
 } // namespace face
diff --git a/daemon/face/tcp-factory.cpp b/daemon/face/tcp-factory.cpp
index 3132796..e2fb3fe 100644
--- a/daemon/face/tcp-factory.cpp
+++ b/daemon/face/tcp-factory.cpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
@@ -97,7 +97,7 @@
       tcp::Endpoint endpoint(ip::tcp::v4(), port);
       shared_ptr<TcpChannel> v4Channel = this->createChannel(endpoint);
       if (wantListen && !v4Channel->isListening()) {
-        v4Channel->listen(context.addFace, nullptr);
+        v4Channel->listen(this->addFace, nullptr);
       }
       providedSchemes.insert("tcp4");
     }
@@ -109,7 +109,7 @@
       tcp::Endpoint endpoint(ip::tcp::v6(), port);
       shared_ptr<TcpChannel> v6Channel = this->createChannel(endpoint);
       if (wantListen && !v6Channel->isListening()) {
-        v6Channel->listen(context.addFace, nullptr);
+        v6Channel->listen(this->addFace, nullptr);
       }
       providedSchemes.insert("tcp6");
     }
diff --git a/daemon/face/udp-factory.cpp b/daemon/face/udp-factory.cpp
index 5b68061..cc02bfc 100644
--- a/daemon/face/udp-factory.cpp
+++ b/daemon/face/udp-factory.cpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
@@ -151,7 +151,7 @@
       udp::Endpoint endpoint(ip::udp::v4(), port);
       shared_ptr<UdpChannel> v4Channel = this->createChannel(endpoint, time::seconds(idleTimeout));
       if (!v4Channel->isListening()) {
-        v4Channel->listen(context.addFace, nullptr);
+        v4Channel->listen(this->addFace, nullptr);
       }
       providedSchemes.insert("udp");
       providedSchemes.insert("udp4");
@@ -164,7 +164,7 @@
       udp::Endpoint endpoint(ip::udp::v6(), port);
       shared_ptr<UdpChannel> v6Channel = this->createChannel(endpoint, time::seconds(idleTimeout));
       if (!v6Channel->isListening()) {
-        v6Channel->listen(context.addFace, nullptr);
+        v6Channel->listen(this->addFace, nullptr);
       }
       providedSchemes.insert("udp");
       providedSchemes.insert("udp6");
@@ -485,7 +485,7 @@
                                                         needIfname ? netif.name : "");
       if (face->getId() == INVALID_FACEID) {
         // new face: register with forwarding
-        context.addFace(face);
+        this->addFace(face);
       }
       else {
         // existing face: don't destroy
diff --git a/daemon/face/unix-stream-factory.cpp b/daemon/face/unix-stream-factory.cpp
index a7aeadb..2d5fe94 100644
--- a/daemon/face/unix-stream-factory.cpp
+++ b/daemon/face/unix-stream-factory.cpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
@@ -74,7 +74,7 @@
   if (!context.isDryRun) {
     auto channel = this->createChannel(path);
     if (!channel->isListening()) {
-      channel->listen(context.addFace, nullptr);
+      channel->listen(this->addFace, nullptr);
     }
   }
 }
diff --git a/daemon/face/websocket-factory.cpp b/daemon/face/websocket-factory.cpp
index a0b58e5..2905ae4 100644
--- a/daemon/face/websocket-factory.cpp
+++ b/daemon/face/websocket-factory.cpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
@@ -105,7 +105,7 @@
 
     auto channel = this->createChannel(endpoint);
     if (!channel->isListening()) {
-      channel->listen(context.addFace);
+      channel->listen(this->addFace);
       if (m_channels.size() > 1) {
         NFD_LOG_WARN("Adding WebSocket channel for new endpoint; cannot close existing channels");
       }
diff --git a/daemon/nfd.cpp b/daemon/nfd.cpp
index 8c58034..8a917a9 100644
--- a/daemon/nfd.cpp
+++ b/daemon/nfd.cpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
@@ -46,30 +46,22 @@
 
 static const std::string INTERNAL_CONFIG = "internal://nfd.conf";
 
-static inline ndn::net::NetworkMonitor*
-makeNetworkMonitor()
+Nfd::Nfd(ndn::KeyChain& keyChain)
+  : m_keyChain(keyChain)
+  , m_netmon(make_shared<ndn::net::NetworkMonitor>(getGlobalIoService()))
 {
-  try {
-    return new ndn::net::NetworkMonitor(getGlobalIoService());
-  }
-  catch (const ndn::net::NetworkMonitor::Error& e) {
-    NFD_LOG_WARN(e.what());
-    return nullptr;
-  }
 }
 
 Nfd::Nfd(const std::string& configFile, ndn::KeyChain& keyChain)
-  : m_configFile(configFile)
-  , m_keyChain(keyChain)
-  , m_networkMonitor(makeNetworkMonitor())
+  : Nfd(keyChain)
 {
+  m_configFile = configFile;
 }
 
 Nfd::Nfd(const ConfigSection& config, ndn::KeyChain& keyChain)
-  : m_configSection(config)
-  , m_keyChain(keyChain)
-  , m_networkMonitor(makeNetworkMonitor())
+  : Nfd(keyChain)
 {
+  m_configSection = config;
 }
 
 // It is necessary to explicitly define the destructor, because some member variables (e.g.,
@@ -87,23 +79,21 @@
   FaceTable& faceTable = m_forwarder->getFaceTable();
   faceTable.addReserved(face::makeNullFace(), face::FACEID_NULL);
   faceTable.addReserved(face::makeNullFace(FaceUri("contentstore://")), face::FACEID_CONTENT_STORE);
-  m_faceSystem.reset(new face::FaceSystem(faceTable));
+  m_faceSystem = make_unique<face::FaceSystem>(faceTable, m_netmon);
 
   initializeManagement();
 
   PrivilegeHelper::drop();
 
-  if (m_networkMonitor) {
-    m_networkMonitor->onNetworkStateChanged.connect([this] {
-        // delay stages, so if multiple events are triggered in short sequence,
-        // only one auto-detection procedure is triggered
-        m_reloadConfigEvent = scheduler::schedule(time::seconds(5),
-          [this] {
-            NFD_LOG_INFO("Network change detected, reloading face section of the config file...");
-            this->reloadConfigFileFaceSection();
-          });
-      });
-  }
+  m_netmon->onNetworkStateChanged.connect([this] {
+      // delay stages, so if multiple events are triggered in short sequence,
+      // only one auto-detection procedure is triggered
+      m_reloadConfigEvent = scheduler::schedule(time::seconds(5),
+        [this] {
+          NFD_LOG_INFO("Network change detected, reloading face section of the config file...");
+          this->reloadConfigFileFaceSection();
+        });
+    });
 }
 
 void
diff --git a/daemon/nfd.hpp b/daemon/nfd.hpp
index 8f83644..ff7b1c4 100644
--- a/daemon/nfd.hpp
+++ b/daemon/nfd.hpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
@@ -93,6 +93,9 @@
   reloadConfigFile();
 
 private:
+  explicit
+  Nfd(ndn::KeyChain& keyChain);
+
   void
   initializeLogging();
 
@@ -119,7 +122,7 @@
   unique_ptr<FibManager> m_fibManager;
   unique_ptr<StrategyChoiceManager> m_strategyChoiceManager;
 
-  unique_ptr<ndn::net::NetworkMonitor> m_networkMonitor;
+  shared_ptr<ndn::net::NetworkMonitor> m_netmon;
   scheduler::ScopedEventId m_reloadConfigEvent;
 };
 
diff --git a/tests/daemon/face/face-system-fixture.hpp b/tests/daemon/face/face-system-fixture.hpp
index 42ed02c..f1ea72c 100644
--- a/tests/daemon/face/face-system-fixture.hpp
+++ b/tests/daemon/face/face-system-fixture.hpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
@@ -32,6 +32,7 @@
 #include "fw/face-table.hpp"
 
 #include "tests/test-common.hpp"
+#include <ndn-cxx/net/network-monitor-stub.hpp>
 
 namespace nfd {
 namespace face {
@@ -43,7 +44,8 @@
 {
 public:
   FaceSystemFixture()
-    : faceSystem(faceTable)
+    : netmon(make_shared<ndn::net::NetworkMonitorStub>(~0))
+    , faceSystem(faceTable, netmon)
   {
     faceSystem.setConfigFile(configFile);
   }
@@ -107,6 +109,7 @@
 protected:
   ConfigFile configFile;
   FaceTable faceTable;
+  shared_ptr<ndn::net::NetworkMonitorStub> netmon;
   FaceSystem faceSystem;
 };