face: Transport::canChangePersistencyTo

Feasibility of transport persistency change can be checked with
Transport::canChangePersistencyTo before executing the actual change.
This enables management to verify persistency and other parameters
in a faces/update command before applying the updates.

Change-Id: Ia283f0daf678f47aad7b78b7e06dee4827f57cab
refs: #3232
diff --git a/daemon/face/ethernet-transport.cpp b/daemon/face/ethernet-transport.cpp
index 5ea385b..f47f1b1 100644
--- a/daemon/face/ethernet-transport.cpp
+++ b/daemon/face/ethernet-transport.cpp
@@ -120,14 +120,6 @@
                                 boost::asio::placeholders::bytes_transferred));
 }
 
-void EthernetTransport::beforeChangePersistency(ndn::nfd::FacePersistency newPersistency)
-{
-  if (newPersistency != ndn::nfd::FACE_PERSISTENCY_PERMANENT) {
-    BOOST_THROW_EXCEPTION(
-      std::invalid_argument("EthernetTransport supports only FACE_PERSISTENCY_PERMANENT"));
-  }
-}
-
 void EthernetTransport::doSend(Transport::Packet&& packet)
 {
   NFD_LOG_FACE_TRACE(__func__);
diff --git a/daemon/face/ethernet-transport.hpp b/daemon/face/ethernet-transport.hpp
index 2b1229a..d89e280 100644
--- a/daemon/face/ethernet-transport.hpp
+++ b/daemon/face/ethernet-transport.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2016,  Regents of the University of California,
+ * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -65,14 +65,11 @@
                     const ethernet::Address& mcastAddress);
 
 protected:
-  virtual void
-  beforeChangePersistency(ndn::nfd::FacePersistency newPersistency) final;
-
-  virtual void
+  void
   doClose() final;
 
 private:
-  virtual void
+  void
   doSend(Transport::Packet&& packet) final;
 
   /**
diff --git a/daemon/face/internal-transport.cpp b/daemon/face/internal-transport.cpp
index cc3e0a6..5b82c20 100644
--- a/daemon/face/internal-transport.cpp
+++ b/daemon/face/internal-transport.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -46,15 +46,6 @@
 }
 
 void
-InternalForwarderTransport::beforeChangePersistency(ndn::nfd::FacePersistency newPersistency)
-{
-  if (newPersistency != ndn::nfd::FACE_PERSISTENCY_PERMANENT) {
-    BOOST_THROW_EXCEPTION(
-      std::invalid_argument("InternalForwarderTransport supports only FACE_PERSISTENCY_PERMANENT"));
-  }
-}
-
-void
 InternalForwarderTransport::receiveFromLink(const Block& packet)
 {
   NFD_LOG_FACE_TRACE(__func__);
diff --git a/daemon/face/internal-transport.hpp b/daemon/face/internal-transport.hpp
index 61a12e7..78f62a2 100644
--- a/daemon/face/internal-transport.hpp
+++ b/daemon/face/internal-transport.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2016,  Regents of the University of California,
+ * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -58,18 +58,15 @@
                              ndn::nfd::FaceScope scope = ndn::nfd::FACE_SCOPE_LOCAL,
                              ndn::nfd::LinkType linkType = ndn::nfd::LINK_TYPE_POINT_TO_POINT);
 
-  virtual void
+  void
   receiveFromLink(const Block& packet) override;
 
 protected:
-  virtual void
-  beforeChangePersistency(ndn::nfd::FacePersistency newPersistency) override;
-
-  virtual void
+  void
   doClose() override;
 
 private:
-  virtual void
+  void
   doSend(Packet&& packet) override;
 
 private:
@@ -92,28 +89,28 @@
   void
   connectToForwarder(InternalForwarderTransport* forwarderTransport);
 
-  virtual void
+  void
   receiveFromLink(const Block& packet) override;
 
-  virtual void
+  void
   close() override
   {
   }
 
-  virtual void
+  void
   pause() override
   {
   }
 
-  virtual void
+  void
   resume() override
   {
   }
 
-  virtual void
+  void
   send(const Block& wire) override;
 
-  virtual void
+  void
   send(const Block& header, const Block& payload) override;
 
 private:
diff --git a/daemon/face/multicast-udp-transport.cpp b/daemon/face/multicast-udp-transport.cpp
index 9503c35..34bef0c 100644
--- a/daemon/face/multicast-udp-transport.cpp
+++ b/daemon/face/multicast-udp-transport.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -51,15 +51,6 @@
 }
 
 void
-MulticastUdpTransport::beforeChangePersistency(ndn::nfd::FacePersistency newPersistency)
-{
-  if (newPersistency != ndn::nfd::FACE_PERSISTENCY_PERMANENT) {
-    BOOST_THROW_EXCEPTION(
-      std::invalid_argument("MulticastUdpTransport supports only FACE_PERSISTENCY_PERMANENT"));
-  }
-}
-
-void
 MulticastUdpTransport::doSend(Transport::Packet&& packet)
 {
   NFD_LOG_FACE_TRACE(__func__);
diff --git a/daemon/face/multicast-udp-transport.hpp b/daemon/face/multicast-udp-transport.hpp
index b16ac6a..2b04eb7 100644
--- a/daemon/face/multicast-udp-transport.hpp
+++ b/daemon/face/multicast-udp-transport.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2016,  Regents of the University of California,
+ * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -57,15 +57,11 @@
                         protocol::socket&& recvSocket,
                         protocol::socket&& sendSocket);
 
-protected:
-  virtual void
-  beforeChangePersistency(ndn::nfd::FacePersistency newPersistency) final;
-
 private:
-  virtual void
+  void
   doSend(Transport::Packet&& packet) final;
 
-  virtual void
+  void
   doClose() final;
 
 private:
diff --git a/daemon/face/tcp-transport.cpp b/daemon/face/tcp-transport.cpp
index f217bb0..c14cab2 100644
--- a/daemon/face/tcp-transport.cpp
+++ b/daemon/face/tcp-transport.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2016,  Regents of the University of California,
+ * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -55,11 +55,17 @@
   NFD_LOG_FACE_INFO("Creating transport");
 }
 
-void
-TcpTransport::beforeChangePersistency(ndn::nfd::FacePersistency newPersistency)
+bool
+TcpTransport::canChangePersistencyToImpl(ndn::nfd::FacePersistency newPersistency) const
 {
-  // if persistency is changing from permanent to any other value
-  if (this->getPersistency() == ndn::nfd::FACE_PERSISTENCY_PERMANENT) {
+  return true;
+}
+
+void
+TcpTransport::afterChangePersistency(ndn::nfd::FacePersistency oldPersistency)
+{
+  // if persistency was changed from permanent to any other value
+  if (oldPersistency == ndn::nfd::FACE_PERSISTENCY_PERMANENT) {
     if (this->getState() == TransportState::DOWN) {
       // non-permanent transport cannot be in DOWN state, so fail hard
       this->setState(TransportState::FAILED);
diff --git a/daemon/face/tcp-transport.hpp b/daemon/face/tcp-transport.hpp
index 83fba82..ffd05a8 100644
--- a/daemon/face/tcp-transport.hpp
+++ b/daemon/face/tcp-transport.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2016,  Regents of the University of California,
+ * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -45,8 +45,11 @@
   TcpTransport(protocol::socket&& socket, ndn::nfd::FacePersistency persistency);
 
 protected:
+  bool
+  canChangePersistencyToImpl(ndn::nfd::FacePersistency newPersistency) const final;
+
   void
-  beforeChangePersistency(ndn::nfd::FacePersistency newPersistency) final;
+  afterChangePersistency(ndn::nfd::FacePersistency oldPersistency) final;
 
   void
   doClose() final;
diff --git a/daemon/face/transport.cpp b/daemon/face/transport.cpp
index 26cc0d7..81f6edf 100644
--- a/daemon/face/transport.cpp
+++ b/daemon/face/transport.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -127,23 +127,47 @@
   m_service->receivePacket(std::move(packet));
 }
 
+bool
+Transport::canChangePersistencyTo(ndn::nfd::FacePersistency newPersistency) const
+{
+  // not changing, or setting initial persistency in subclass constructor
+  if (m_persistency == newPersistency || m_persistency == ndn::nfd::FACE_PERSISTENCY_NONE) {
+    return true;
+  }
+
+  if (newPersistency == ndn::nfd::FACE_PERSISTENCY_NONE) {
+    NFD_LOG_FACE_TRACE("cannot change persistency to NONE");
+    return false;
+  }
+
+  return this->canChangePersistencyToImpl(newPersistency);
+}
+
+bool
+Transport::canChangePersistencyToImpl(ndn::nfd::FacePersistency newPersistency) const
+{
+  return false;
+}
+
 void
 Transport::setPersistency(ndn::nfd::FacePersistency newPersistency)
 {
+  BOOST_ASSERT(canChangePersistencyTo(newPersistency));
+
   if (m_persistency == newPersistency) {
     return;
   }
 
-  if (newPersistency == ndn::nfd::FACE_PERSISTENCY_NONE) {
-    throw std::runtime_error("invalid persistency transition");
-  }
+  NFD_LOG_FACE_INFO("setPersistency " << m_persistency << " -> " << newPersistency);
 
-  if (m_persistency != ndn::nfd::FACE_PERSISTENCY_NONE) {
-    this->beforeChangePersistency(newPersistency);
-    NFD_LOG_FACE_DEBUG("setPersistency " << m_persistency << " -> " << newPersistency);
-  }
-
+  auto oldPersistency = m_persistency;
   m_persistency = newPersistency;
+  this->afterChangePersistency(oldPersistency);
+}
+
+void
+Transport::afterChangePersistency(ndn::nfd::FacePersistency oldPersistency)
+{
 }
 
 void
diff --git a/daemon/face/transport.hpp b/daemon/face/transport.hpp
index a12ce7c..3db4714 100644
--- a/daemon/face/transport.hpp
+++ b/daemon/face/transport.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -221,6 +221,20 @@
   ndn::nfd::FacePersistency
   getPersistency() const;
 
+  /** \brief check whether the intended change from the current persistency to \p newPersistency
+   *  can be performed
+   *
+   *  This function serves as an external API, and invokes the internal function
+   *  canChangePersistencyToImpl to perform further checks if \p newPersistency differs from
+   *  the current persistency.
+   *
+   *  \pre getPersistency() != NONE
+   *
+   *  \return true if the intended change can be performed, otherwise false
+   */
+  bool
+  canChangePersistencyTo(ndn::nfd::FacePersistency newPersistency) const;
+
   /** \brief changes face persistency setting
    */
   void
@@ -289,12 +303,23 @@
   setExpirationTime(const time::steady_clock::TimePoint& expirationTime);
 
 protected: // to be overridden by subclass
-  /** \brief invoked before persistency is changed
-   *  \throw std::invalid_argument new persistency is not supported
-   *  \throw std::runtime_error transition is disallowed
+  /** \brief invoked by canChangePersistencyTo to perform the check
+   *
+   *  Base class implementation returns false.
+   *
+   *  \param newPersistency the new persistency, guaranteed to be different from current persistency
+   */
+  virtual bool
+  canChangePersistencyToImpl(ndn::nfd::FacePersistency newPersistency) const;
+
+  /** \brief invoked after the persistency has been changed
+   *
+   *  The base class implementation does nothing.
+   *  When overridden in a subclass, the function should update internal states
+   *  after persistency setting has been changed.
    */
   virtual void
-  beforeChangePersistency(ndn::nfd::FacePersistency newPersistency) = 0;
+  afterChangePersistency(ndn::nfd::FacePersistency oldPersistency);
 
   /** \brief performs Transport specific operations to close the transport
    *
diff --git a/daemon/face/unicast-udp-transport.cpp b/daemon/face/unicast-udp-transport.cpp
index b6ad19a..2860559 100644
--- a/daemon/face/unicast-udp-transport.cpp
+++ b/daemon/face/unicast-udp-transport.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -80,10 +80,16 @@
   }
 }
 
-void
-UnicastUdpTransport::beforeChangePersistency(ndn::nfd::FacePersistency newPersistency)
+bool
+UnicastUdpTransport::canChangePersistencyToImpl(ndn::nfd::FacePersistency newPersistency) const
 {
-  if (newPersistency == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND &&
+  return true;
+}
+
+void
+UnicastUdpTransport::afterChangePersistency(ndn::nfd::FacePersistency oldPersistency)
+{
+  if (getPersistency() == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND &&
       m_idleTimeout > time::nanoseconds::zero()) {
     scheduleClosureWhenIdle();
   }
diff --git a/daemon/face/unicast-udp-transport.hpp b/daemon/face/unicast-udp-transport.hpp
index 80cd70b..6136746 100644
--- a/daemon/face/unicast-udp-transport.hpp
+++ b/daemon/face/unicast-udp-transport.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2016,  Regents of the University of California,
+ * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -43,8 +43,11 @@
                       time::nanoseconds idleTimeout);
 
 protected:
-  virtual void
-  beforeChangePersistency(ndn::nfd::FacePersistency newPersistency) final;
+  bool
+  canChangePersistencyToImpl(ndn::nfd::FacePersistency newPersistency) const final;
+
+  void
+  afterChangePersistency(ndn::nfd::FacePersistency oldPersistency) final;
 
 private:
   void
diff --git a/daemon/face/unix-stream-transport.cpp b/daemon/face/unix-stream-transport.cpp
index 6df0efb..cbbda54 100644
--- a/daemon/face/unix-stream-transport.cpp
+++ b/daemon/face/unix-stream-transport.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -49,14 +49,5 @@
   NFD_LOG_FACE_INFO("Creating transport");
 }
 
-void
-UnixStreamTransport::beforeChangePersistency(ndn::nfd::FacePersistency newPersistency)
-{
-  if (newPersistency != ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) {
-    BOOST_THROW_EXCEPTION(
-      std::invalid_argument("UnixStreamTransport supports only FACE_PERSISTENCY_ON_DEMAND"));
-  }
-}
-
 } // namespace face
 } // namespace nfd
diff --git a/daemon/face/unix-stream-transport.hpp b/daemon/face/unix-stream-transport.hpp
index e72c01f..510350e 100644
--- a/daemon/face/unix-stream-transport.hpp
+++ b/daemon/face/unix-stream-transport.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2016,  Regents of the University of California,
+ * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -43,10 +43,6 @@
 public:
   explicit
   UnixStreamTransport(protocol::socket&& socket);
-
-protected:
-  virtual void
-  beforeChangePersistency(ndn::nfd::FacePersistency newPersistency) final;
 };
 
 } // namespace face
diff --git a/daemon/face/websocket-transport.cpp b/daemon/face/websocket-transport.cpp
index 2fc331c..68fd08a 100644
--- a/daemon/face/websocket-transport.cpp
+++ b/daemon/face/websocket-transport.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2016,  Regents of the University of California,
+ * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -77,15 +77,6 @@
 }
 
 void
-WebSocketTransport::beforeChangePersistency(ndn::nfd::FacePersistency newPersistency)
-{
-  if (newPersistency != ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) {
-    BOOST_THROW_EXCEPTION(
-      std::invalid_argument("WebSocketTransport supports only FACE_PERSISTENCY_ON_DEMAND"));
-  }
-}
-
-void
 WebSocketTransport::doSend(Transport::Packet&& packet)
 {
   NFD_LOG_FACE_TRACE(__func__);
diff --git a/daemon/face/websocket-transport.hpp b/daemon/face/websocket-transport.hpp
index ea67ea2..c957802 100644
--- a/daemon/face/websocket-transport.hpp
+++ b/daemon/face/websocket-transport.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2016,  Regents of the University of California,
+ * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -63,8 +63,8 @@
                      websocket::Server& server,
                      time::milliseconds pingInterval);
 
-  virtual const Counters&
-  getCounters() const override;
+  const Counters&
+  getCounters() const final;
 
   /** \brief Translates a message into a Block
    *         and delivers it to the link service
@@ -79,14 +79,11 @@
   handlePongTimeout();
 
 protected:
-  virtual void
-  beforeChangePersistency(ndn::nfd::FacePersistency newPersistency) final;
-
-  virtual void
+  void
   doClose() final;
 
 private:
-  virtual void
+  void
   doSend(Transport::Packet&& packet) final;
 
   void