lsdb: emit signals when modified

RoutingTable and NamePrefixTable consume the signal
and behave accordingly.

refs: #4127

Change-Id: I6540f30f0222f804b01dc7d9640831c84e5264cc
diff --git a/src/lsa/adj-lsa.cpp b/src/lsa/adj-lsa.cpp
index 781422c..3d49533 100644
--- a/src/lsa/adj-lsa.cpp
+++ b/src/lsa/adj-lsa.cpp
@@ -124,7 +124,7 @@
 AdjLsa::toString() const
 {
   std::ostringstream os;
-  os << Lsa::toString();
+  os << getString();
   os << "      Adjacent(s):\n";
 
   int adjacencyIndex = 0;
@@ -139,6 +139,20 @@
   return os.str();
 }
 
+std::tuple<bool, std::list<ndn::Name>, std::list<ndn::Name>>
+AdjLsa::update(const std::shared_ptr<Lsa>& lsa)
+{
+  auto alsa = std::static_pointer_cast<AdjLsa>(lsa);
+  if (!isEqualContent(*alsa)) {
+    resetAdl();
+    for (const auto& adjacent : alsa->getAdl()) {
+      addAdjacent(adjacent);
+    }
+    return std::make_tuple(true, std::list<ndn::Name>{}, std::list<ndn::Name>{});
+  }
+  return std::make_tuple(false, std::list<ndn::Name>{}, std::list<ndn::Name>{});
+}
+
 std::ostream&
 operator<<(std::ostream& os, const AdjLsa& lsa)
 {
diff --git a/src/lsa/adj-lsa.hpp b/src/lsa/adj-lsa.hpp
index 9857cc8..43562a6 100644
--- a/src/lsa/adj-lsa.hpp
+++ b/src/lsa/adj-lsa.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2020,  The University of Memphis,
+ * Copyright (c) 2014-2021,  The University of Memphis,
  *                           Regents of the University of California,
  *                           Arizona Board of Regents.
  *
@@ -113,6 +113,9 @@
   std::string
   toString() const override;
 
+  std::tuple<bool, std::list<ndn::Name>, std::list<ndn::Name>>
+  update(const std::shared_ptr<Lsa>& lsa) override;
+
 private:
   uint32_t m_noLink;
 
diff --git a/src/lsa/coordinate-lsa.cpp b/src/lsa/coordinate-lsa.cpp
index 32172e6..f61fb29 100644
--- a/src/lsa/coordinate-lsa.cpp
+++ b/src/lsa/coordinate-lsa.cpp
@@ -144,7 +144,7 @@
 CoordinateLsa::toString() const
 {
   std::ostringstream os;
-  os << Lsa::toString();
+  os << getString();
   os << "      Hyperbolic Radius  : " << m_hyperbolicRadius << "\n";
   int i = 0;
   for (const auto& value : m_hyperbolicAngles) {
@@ -154,6 +154,21 @@
   return os.str();
 }
 
+std::tuple<bool, std::list<ndn::Name>, std::list<ndn::Name>>
+CoordinateLsa::update(const std::shared_ptr<Lsa>& lsa)
+{
+  auto clsa = std::static_pointer_cast<CoordinateLsa>(lsa);
+  if (!isEqualContent(*clsa)) {
+    m_hyperbolicRadius = clsa->getCorRadius();
+    m_hyperbolicAngles.clear();
+    for (const auto& angle : clsa->getCorTheta()) {
+      m_hyperbolicAngles.push_back(angle);
+    }
+    return std::make_tuple(true, std::list<ndn::Name>{}, std::list<ndn::Name>{});
+  }
+  return std::make_tuple(false, std::list<ndn::Name>{}, std::list<ndn::Name>{});
+}
+
 std::ostream&
 operator<<(std::ostream& os, const CoordinateLsa& lsa)
 {
diff --git a/src/lsa/coordinate-lsa.hpp b/src/lsa/coordinate-lsa.hpp
index 4b07627..c1d3a13 100644
--- a/src/lsa/coordinate-lsa.hpp
+++ b/src/lsa/coordinate-lsa.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2020,  The University of Memphis,
+ * Copyright (c) 2014-2021,  The University of Memphis,
  *                           Regents of the University of California,
  *                           Arizona Board of Regents.
  *
@@ -98,6 +98,9 @@
   std::string
   toString() const override;
 
+  std::tuple<bool, std::list<ndn::Name>, std::list<ndn::Name>>
+  update(const std::shared_ptr<Lsa>& lsa) override;
+
 private:
   double m_hyperbolicRadius = 0.0;
   std::vector<double> m_hyperbolicAngles;
diff --git a/src/lsa/lsa.cpp b/src/lsa/lsa.cpp
index 25841d2..c633549 100644
--- a/src/lsa/lsa.cpp
+++ b/src/lsa/lsa.cpp
@@ -35,6 +35,13 @@
 {
 }
 
+Lsa::Lsa(const Lsa& lsa)
+  : m_originRouter(lsa.getOriginRouter())
+  , m_seqNo(lsa.getSeqNo())
+  , m_expirationTimePoint(lsa.getExpirationTimePoint())
+{
+}
+
 template<ndn::encoding::Tag TAG>
 size_t
 Lsa::wireEncode(ndn::EncodingImpl<TAG>& encoder) const
@@ -138,13 +145,13 @@
 }
 
 std::string
-Lsa::toString() const
+Lsa::getString() const
 {
   std::ostringstream os;
-  auto duration = getExpirationTimePoint() - ndn::time::system_clock::now();
+  auto duration = m_expirationTimePoint - ndn::time::system_clock::now();
   os << "    " << getType() << " LSA:\n"
-     << "      Origin Router      : " << getOriginRouter() << "\n"
-     << "      Sequence Number    : " << getSeqNo() << "\n"
+     << "      Origin Router      : " << m_originRouter << "\n"
+     << "      Sequence Number    : " << m_seqNo << "\n"
      << "      Expires in         : " << ndn::time::duration_cast<ndn::time::milliseconds>(duration)
      << "\n";
   return os.str();
diff --git a/src/lsa/lsa.hpp b/src/lsa/lsa.hpp
index 7fb1058..89ddb85 100644
--- a/src/lsa/lsa.hpp
+++ b/src/lsa/lsa.hpp
@@ -60,6 +60,8 @@
 
   Lsa() = default;
 
+  Lsa(const Lsa& lsa);
+
 public:
   virtual
   ~Lsa() = default;
@@ -108,19 +110,16 @@
   void
   setExpiringEventId(ndn::scheduler::EventId eid)
   {
-    m_expiringEventId = std::move(eid);
+    m_expiringEventId = eid;
   }
 
-  ndn::scheduler::EventId
-  getExpiringEventId() const
-  {
-    return m_expiringEventId;
-  }
-
-  /*! Get data common to all LSA types.
+  /*! Get data common to all LSA types for printing purposes.
    */
   virtual std::string
-  toString() const;
+  toString() const = 0;
+
+  virtual std::tuple<bool, std::list<ndn::Name>, std::list<ndn::Name>>
+  update(const std::shared_ptr<Lsa>& lsa) = 0;
 
   virtual const ndn::Block&
   wireEncode() const = 0;
@@ -133,11 +132,14 @@
   void
   wireDecode(const ndn::Block& wire);
 
+  std::string
+  getString() const;
+
 PUBLIC_WITH_TESTS_ELSE_PROTECTED:
   ndn::Name m_originRouter;
   uint64_t m_seqNo = 0;
   ndn::time::system_clock::TimePoint m_expirationTimePoint;
-  ndn::scheduler::EventId m_expiringEventId;
+  ndn::scheduler::ScopedEventId m_expiringEventId;
 
   mutable ndn::Block m_wire;
 };
diff --git a/src/lsa/name-lsa.cpp b/src/lsa/name-lsa.cpp
index 173b9c7..266649a 100644
--- a/src/lsa/name-lsa.cpp
+++ b/src/lsa/name-lsa.cpp
@@ -122,7 +122,7 @@
 NameLsa::toString() const
 {
   std::ostringstream os;
-  os << Lsa::toString();
+  os << getString();
   os << "      Names:\n";
   int i = 0;
   for (const auto& name : m_npl.getNames()) {
@@ -132,6 +132,38 @@
   return os.str();
 }
 
+std::tuple<bool, std::list<ndn::Name>, std::list<ndn::Name>>
+NameLsa::update(const std::shared_ptr<Lsa>& lsa)
+{
+  auto nlsa = std::static_pointer_cast<NameLsa>(lsa);
+  bool updated = false;
+
+  // Obtain the set difference of the current and the incoming
+  // name prefix sets, and add those.
+  std::list<ndn::Name> newNames = nlsa->getNpl().getNames();
+  std::list<ndn::Name> oldNames = m_npl.getNames();
+  std::list<ndn::Name> namesToAdd;
+  std::set_difference(newNames.begin(), newNames.end(), oldNames.begin(), oldNames.end(),
+                      std::inserter(namesToAdd, namesToAdd.begin()));
+  for (const auto& name : namesToAdd) {
+    addName(name);
+    updated = true;
+  }
+
+  m_npl.sort();
+
+  // Also remove any names that are no longer being advertised.
+  std::list<ndn::Name> namesToRemove;
+  std::set_difference(oldNames.begin(), oldNames.end(), newNames.begin(), newNames.end(),
+                      std::inserter(namesToRemove, namesToRemove.begin()));
+  for (const auto& name : namesToRemove) {
+    removeName(name);
+    updated = true;
+  }
+
+  return std::make_tuple(updated, namesToAdd, namesToRemove);
+}
+
 std::ostream&
 operator<<(std::ostream& os, const NameLsa& lsa)
 {
diff --git a/src/lsa/name-lsa.hpp b/src/lsa/name-lsa.hpp
index 5e9c8d5..5d6e02b 100644
--- a/src/lsa/name-lsa.hpp
+++ b/src/lsa/name-lsa.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2020,  The University of Memphis,
+ * Copyright (c) 2014-2021,  The University of Memphis,
  *                           Regents of the University of California,
  *                           Arizona Board of Regents.
  *
@@ -97,6 +97,9 @@
   std::string
   toString() const override;
 
+  std::tuple<bool, std::list<ndn::Name>, std::list<ndn::Name>>
+  update(const std::shared_ptr<Lsa>& lsa) override;
+
 private:
   NamePrefixList m_npl;
 };
diff --git a/src/lsdb.cpp b/src/lsdb.cpp
index a2f552d..5e416b3 100644
--- a/src/lsdb.cpp
+++ b/src/lsdb.cpp
@@ -32,13 +32,10 @@
 const ndn::time::steady_clock::TimePoint Lsdb::DEFAULT_LSA_RETRIEVAL_DEADLINE =
   ndn::time::steady_clock::TimePoint::min();
 
-Lsdb::Lsdb(ndn::Face& face, ndn::KeyChain& keyChain, ConfParameter& confParam,
-           NamePrefixTable& namePrefixTable, RoutingTable& routingTable)
+Lsdb::Lsdb(ndn::Face& face, ndn::KeyChain& keyChain, ConfParameter& confParam)
   : m_face(face)
   , m_scheduler(face.getIoService())
   , m_confParam(confParam)
-  , m_namePrefixTable(namePrefixTable)
-  , m_routingTable(routingTable)
   , m_sync(m_face,
            [this] (const ndn::Name& routerName, const Lsa::Type& lsaType,
                    const uint64_t& sequenceNumber) {
@@ -56,9 +53,26 @@
         expressInterest(lsaInterest, 0);
       }))
   , m_segmentPublisher(m_face, keyChain)
-  , m_isBuildAdjLsaSheduled(false)
+  , m_isBuildAdjLsaScheduled(false)
   , m_adjBuildCount(0)
 {
+  ndn::Name name = m_confParam.getLsaPrefix();
+  NLSR_LOG_DEBUG("Setting interest filter for LsaPrefix: " << name);
+
+  m_face.setInterestFilter(ndn::InterestFilter(name).allowLoopback(false),
+    [this] (const auto& name, const auto& interest) { processInterest(name, interest); },
+    [] (const auto& name) { NLSR_LOG_DEBUG("Successfully registered prefix: " << name); },
+    [] (const auto& name, const auto& reason) {
+      NLSR_LOG_ERROR("Failed to register prefix " << name);
+      NDN_THROW(std::runtime_error("Register prefix failed: " + reason));
+    },
+    m_confParam.getSigningInfo(), ndn::nfd::ROUTE_FLAG_CAPTURE);
+
+  buildAndInstallOwnNameLsa();
+  // Install coordinate LSAs if using HR or dry-run HR.
+  if (m_confParam.getHyperbolicState() != HYPERBOLIC_STATE_OFF) {
+    buildAndInstallOwnCoordinateLsa();
+  }
 }
 
 void
@@ -101,44 +115,37 @@
     return;
   }
 
-  if (m_isBuildAdjLsaSheduled) {
+  if (m_isBuildAdjLsaScheduled) {
     NLSR_LOG_DEBUG("Rescheduling Adjacency LSA build in " << m_adjLsaBuildInterval);
   }
   else {
     NLSR_LOG_DEBUG("Scheduling Adjacency LSA build in " << m_adjLsaBuildInterval);
-    m_isBuildAdjLsaSheduled = true;
+    m_isBuildAdjLsaScheduled = true;
   }
   m_scheduledAdjLsaBuild = m_scheduler.schedule(m_adjLsaBuildInterval, [this] { buildAdjLsa(); });
 }
 
-template<typename T>
 void
 Lsdb::writeLog() const
 {
-  if ((T::type() == Lsa::Type::COORDINATE &&
-      m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_OFF) ||
-      (T::type() == Lsa::Type::ADJACENCY &&
-      m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_ON)) {
-    return;
-  }
+  static const Lsa::Type types[] = {Lsa::Type::COORDINATE, Lsa::Type::NAME, Lsa::Type::ADJACENCY};
+  for (const auto& type : types) {
+    if ((type == Lsa::Type::COORDINATE &&
+         m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_OFF) ||
+        (type == Lsa::Type::ADJACENCY &&
+       m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_ON)) {
+      continue;
+    }
 
-  NLSR_LOG_DEBUG("---------------" << T::type() << " LSDB-------------------");
-  auto lsaRange = m_lsdb.get<byType>().equal_range(T::type());
-  for (auto lsaIt = lsaRange.first; lsaIt != lsaRange.second; ++lsaIt) {
-    auto lsaPtr = std::static_pointer_cast<T>(*lsaIt);
-    NLSR_LOG_DEBUG(lsaPtr->toString());
+    NLSR_LOG_DEBUG("---------------" << type << " LSDB-------------------");
+    auto lsaRange = m_lsdb.get<byType>().equal_range(type);
+    for (auto lsaIt = lsaRange.first; lsaIt != lsaRange.second; ++lsaIt) {
+      NLSR_LOG_DEBUG((*lsaIt)->toString());
+    }
   }
 }
 
 void
-Lsdb::writeLog() const
-{
-  writeLog<CoordinateLsa>();
-  writeLog<NameLsa>();
-  writeLog<AdjLsa>();
-}
-
-void
 Lsdb::processInterest(const ndn::Name& name, const ndn::Interest& interest)
 {
   ndn::Name interestName(interest.getName());
@@ -199,8 +206,7 @@
   if (auto lsaPtr = findLsa(originRouter, lsaType)) {
     NLSR_LOG_TRACE("Verifying SeqNo for " << lsaType << " is same as requested.");
     if (lsaPtr->getSeqNo() == seqNo) {
-      m_segmentPublisher.publish(interest.getName(), interest.getName(),
-                                 lsaPtr->wireEncode(),
+      m_segmentPublisher.publish(interest.getName(), interest.getName(), lsaPtr->wireEncode(),
                                  m_lsaRefreshTime, m_confParam.getSigningInfo());
       incrementDataSentStats(lsaType);
       return true;
@@ -216,39 +222,21 @@
 Lsdb::installLsa(std::shared_ptr<Lsa> lsa)
 {
   auto timeToExpire = m_lsaRefreshTime;
+  if (lsa->getOriginRouter() != m_thisRouterPrefix) {
+    auto duration = lsa->getExpirationTimePoint() - ndn::time::system_clock::now();
+    if (duration > ndn::time::seconds(0)) {
+      timeToExpire = ndn::time::duration_cast<ndn::time::seconds>(duration);
+    }
+  }
 
   auto chkLsa = findLsa(lsa->getOriginRouter(), lsa->getType());
   if (chkLsa == nullptr) {
     NLSR_LOG_DEBUG("Adding " << lsa->getType() << " LSA");
     NLSR_LOG_DEBUG(lsa->toString());
-    ndn::time::seconds timeToExpire = m_lsaRefreshTime;
 
     m_lsdb.emplace(lsa);
 
-    // Add any new name prefixes to the NPT if from another router
-    if (lsa->getOriginRouter() != m_thisRouterPrefix) {
-      // Pass the origin router as both the name to register and where it came from.
-      m_namePrefixTable.addEntry(lsa->getOriginRouter(), lsa->getOriginRouter());
-
-      if (lsa->getType() == Lsa::Type::NAME) {
-        auto nlsa = std::static_pointer_cast<NameLsa>(lsa);
-        for (const auto& name : nlsa->getNpl().getNames()) {
-          if (name != m_thisRouterPrefix) {
-            m_namePrefixTable.addEntry(name, nlsa->getOriginRouter());
-          }
-        }
-      }
-
-      auto duration = lsa->getExpirationTimePoint() - ndn::time::system_clock::now();
-      if (duration > ndn::time::seconds(0)) {
-        timeToExpire = ndn::time::duration_cast<ndn::time::seconds>(duration);
-      }
-    }
-
-    if ((lsa->getType() == Lsa::Type::ADJACENCY && m_confParam.getHyperbolicState() != HYPERBOLIC_STATE_ON)||
-        (lsa->getType() == Lsa::Type::COORDINATE && m_confParam.getHyperbolicState() != HYPERBOLIC_STATE_OFF)) {
-      m_routingTable.scheduleRoutingTableCalculation();
-    }
+    onLsdbModified(lsa, LsdbUpdate::INSTALLED, {}, {});
 
     lsa->setExpiringEventId(scheduleLsaExpiration(lsa, timeToExpire));
   }
@@ -259,104 +247,36 @@
     chkLsa->setSeqNo(lsa->getSeqNo());
     chkLsa->setExpirationTimePoint(lsa->getExpirationTimePoint());
 
-    if (lsa->getType() == Lsa::Type::NAME) {
-      auto chkNameLsa = std::static_pointer_cast<NameLsa>(chkLsa);
-      auto nlsa = std::static_pointer_cast<NameLsa>(lsa);
-      chkNameLsa->getNpl().sort();
-      nlsa->getNpl().sort();
-      if (!chkNameLsa->isEqualContent(*nlsa)) {
-        // Obtain the set difference of the current and the incoming
-        // name prefix sets, and add those.
-        std::list<ndn::Name> newNames = nlsa->getNpl().getNames();
-        std::list<ndn::Name> oldNames = chkNameLsa->getNpl().getNames();
-        std::list<ndn::Name> namesToAdd;
-        std::set_difference(newNames.begin(), newNames.end(), oldNames.begin(), oldNames.end(),
-                            std::inserter(namesToAdd, namesToAdd.begin()));
-        for (const auto& name : namesToAdd) {
-          chkNameLsa->addName(name);
-          if (nlsa->getOriginRouter() != m_thisRouterPrefix && name != m_thisRouterPrefix) {
-            m_namePrefixTable.addEntry(name, nlsa->getOriginRouter());
-          }
-        }
+    bool updated;
+    std::list<ndn::Name> namesToAdd, namesToRemove;
+    std::tie(updated, namesToAdd, namesToRemove) = chkLsa->update(lsa);
 
-        chkNameLsa->getNpl().sort();
-
-        // Also remove any names that are no longer being advertised.
-        std::list<ndn::Name> namesToRemove;
-        std::set_difference(oldNames.begin(), oldNames.end(), newNames.begin(), newNames.end(),
-                            std::inserter(namesToRemove, namesToRemove.begin()));
-        for (const auto& name : namesToRemove) {
-          NLSR_LOG_DEBUG("Removing name" << name << " from Name LSA no longer advertised.");
-          chkNameLsa->removeName(name);
-          if (nlsa->getOriginRouter() != m_thisRouterPrefix && name != m_thisRouterPrefix) {
-            m_namePrefixTable.removeEntry(name, nlsa->getOriginRouter());
-          }
-        }
-      }
-    }
-    else if (lsa->getType() == Lsa::Type::ADJACENCY) {
-      auto chkAdjLsa = std::static_pointer_cast<AdjLsa>(chkLsa);
-      auto alsa = std::static_pointer_cast<AdjLsa>(lsa);
-      if (!chkAdjLsa->isEqualContent(*alsa)) {
-        chkAdjLsa->resetAdl();
-        for (const auto& adjacent : alsa->getAdl()) {
-          chkAdjLsa->addAdjacent(adjacent);
-        }
-        m_routingTable.scheduleRoutingTableCalculation();
-      }
-    }
-    else {
-      auto chkCorLsa = std::static_pointer_cast<CoordinateLsa>(chkLsa);
-      auto clsa = std::static_pointer_cast<CoordinateLsa>(lsa);
-      if (!chkCorLsa->isEqualContent(*clsa)) {
-        chkCorLsa->setCorRadius(clsa->getCorRadius());
-        chkCorLsa->setCorTheta(clsa->getCorTheta());
-        if (m_confParam.getHyperbolicState() != HYPERBOLIC_STATE_OFF) {
-          m_routingTable.scheduleRoutingTableCalculation();
-        }
-      }
+    if (updated) {
+      onLsdbModified(lsa, LsdbUpdate::UPDATED, namesToAdd, namesToRemove);
     }
 
-    if (chkLsa->getOriginRouter() != m_thisRouterPrefix) {
-      auto duration = lsa->getExpirationTimePoint() - ndn::time::system_clock::now();
-      if (duration > ndn::time::seconds(0)) {
-        timeToExpire = ndn::time::duration_cast<ndn::time::seconds>(duration);
-      }
-    }
-    chkLsa->getExpiringEventId().cancel();
     chkLsa->setExpiringEventId(scheduleLsaExpiration(chkLsa, timeToExpire));
     NLSR_LOG_DEBUG("Updated " << lsa->getType() << " LSA:");
     NLSR_LOG_DEBUG(chkLsa->toString());
   }
 }
 
-bool
-Lsdb::removeLsa(const ndn::Name& router, Lsa::Type lsaType)
+void
+Lsdb::removeLsa(const LsaContainer::index<Lsdb::byName>::type::iterator& lsaIt)
 {
-  auto lsaIt = m_lsdb.get<byName>().find(std::make_tuple(router, lsaType));
-
   if (lsaIt != m_lsdb.end()) {
     auto lsaPtr = *lsaIt;
-    NLSR_LOG_DEBUG("Removing " << lsaType << " LSA:");
+    NLSR_LOG_DEBUG("Removing " << lsaPtr->getType() << " LSA:");
     NLSR_LOG_DEBUG(lsaPtr->toString());
-    // If the requested name LSA is not ours, we also need to remove
-    // its entries from the NPT.
-    if (lsaPtr->getOriginRouter() != m_thisRouterPrefix) {
-      m_namePrefixTable.removeEntry(lsaPtr->getOriginRouter(), lsaPtr->getOriginRouter());
-
-      if (lsaType == Lsa::Type::NAME) {
-        auto nlsaPtr = std::static_pointer_cast<NameLsa>(lsaPtr);
-        for (const auto& name : nlsaPtr->getNpl().getNames()) {
-          if (name != m_thisRouterPrefix) {
-            m_namePrefixTable.removeEntry(name, nlsaPtr->getOriginRouter());
-          }
-        }
-      }
-    }
     m_lsdb.erase(lsaIt);
-    return true;
+    onLsdbModified(lsaPtr, LsdbUpdate::REMOVED, {}, {});
   }
-  return false;
+}
+
+void
+Lsdb::removeLsa(const ndn::Name& router, Lsa::Type lsaType)
+{
+  removeLsa(m_lsdb.get<byName>().find(std::make_tuple(router, lsaType)));
 }
 
 void
@@ -364,7 +284,7 @@
 {
   NLSR_LOG_TRACE("buildAdjLsa called");
 
-  m_isBuildAdjLsaSheduled = false;
+  m_isBuildAdjLsaScheduled = false;
 
   if (m_confParam.getAdjacencyList().isAdjLsaBuildable(m_confParam.getInterestRetryNumber())) {
 
@@ -382,10 +302,7 @@
       // routers to delete it, too.
       else {
         NLSR_LOG_DEBUG("Removing own Adj LSA; no ACTIVE neighbors");
-
         removeLsa(m_thisRouterPrefix, Lsa::Type::ADJACENCY);
-        // Recompute routing table after removal
-        m_routingTable.scheduleRoutingTableCalculation();
       }
       // In the case that during building the adj LSA, the FIB has to
       // wait on an Interest response, the number of scheduled adj LSA
@@ -397,7 +314,7 @@
   // neighbor, so schedule a build for later (when all that has
   // hopefully finished)
   else {
-    m_isBuildAdjLsaSheduled = true;
+    m_isBuildAdjLsaScheduled = true;
     auto schedulingTime = ndn::time::seconds(m_confParam.getInterestRetryNumber() *
                                              m_confParam.getInterestResendTime());
     m_scheduledAdjLsaBuild = m_scheduler.schedule(schedulingTime, [this] { buildAdjLsa(); });
@@ -422,6 +339,13 @@
   installLsa(std::make_shared<AdjLsa>(adjLsa));
 }
 
+ndn::scheduler::EventId
+Lsdb::scheduleLsaExpiration(std::shared_ptr<Lsa> lsa, ndn::time::seconds expTime)
+{
+  NLSR_LOG_DEBUG("Scheduling expiration in: " << expTime + GRACE_PERIOD << " for " << lsa->getOriginRouter());
+  return m_scheduler.schedule(expTime + GRACE_PERIOD, [this, lsa] { expireOrRefreshLsa(lsa); });
+}
+
 void
 Lsdb::expireOrRefreshLsa(std::shared_ptr<Lsa> lsa)
 {
@@ -454,7 +378,7 @@
       // Since we cannot refresh other router's LSAs, our only choice is to expire.
       else {
         NLSR_LOG_DEBUG("Other's " << lsaPtr->getType() << " LSA, so removing from LSDB");
-        removeLsa(lsaPtr->getOriginRouter(), lsaPtr->getType());
+        removeLsa(lsaIt);
       }
     }
   }
@@ -581,7 +505,6 @@
     ndn::Name originRouter = m_confParam.getNetwork();
     originRouter.append(interestName.getSubName(lsaPosition + 1,
                                                 interestName.size() - lsaPosition - 3));
-
     try {
       Lsa::Type interestedLsType;
       std::istringstream(interestName[-2].toUri()) >> interestedLsType;
@@ -613,7 +536,6 @@
     }
     catch (const std::exception& e) {
       NLSR_LOG_TRACE("LSA data decoding error :( " << e.what());
-      return;
     }
   }
 }
diff --git a/src/lsdb.hpp b/src/lsdb.hpp
index 9a5f147..7d77cd3 100644
--- a/src/lsdb.hpp
+++ b/src/lsdb.hpp
@@ -31,7 +31,6 @@
 #include "test-access-control.hpp"
 #include "communication/sync-logic-handler.hpp"
 #include "statistics.hpp"
-#include "route/name-prefix-table.hpp"
 
 #include <ndn-cxx/security/key-chain.hpp>
 #include <ndn-cxx/util/signal.hpp>
@@ -51,11 +50,16 @@
 
 static constexpr ndn::time::seconds GRACE_PERIOD = 10_s;
 
+enum class LsdbUpdate {
+  INSTALLED,
+  UPDATED,
+  REMOVED
+};
+
 class Lsdb
 {
 public:
-  Lsdb(ndn::Face& face, ndn::KeyChain& keyChain, ConfParameter& confParam,
-       NamePrefixTable& namePrefixTable, RoutingTable& routingTable);
+  Lsdb(ndn::Face& face, ndn::KeyChain& keyChain, ConfParameter& confParam);
 
   ~Lsdb()
   {
@@ -78,18 +82,16 @@
   void
   buildAndInstallOwnNameLsa();
 
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   /*! \brief Builds a cor. LSA for this router and installs it into the LSDB. */
   void
   buildAndInstallOwnCoordinateLsa();
 
+public:
   /*! \brief Schedules a build of this router's LSA. */
   void
   scheduleAdjLsaBuild();
 
-  template<typename T>
-  void
-  writeLog() const;
-
   void
   writeLog() const;
 
@@ -103,9 +105,9 @@
   processInterest(const ndn::Name& name, const ndn::Interest& interest);
 
   bool
-  getIsBuildAdjLsaSheduled() const
+  getIsBuildAdjLsaScheduled() const
   {
-    return m_isBuildAdjLsaSheduled;
+    return m_isBuildAdjLsaScheduled;
   }
 
   SyncLogicHandler&
@@ -238,9 +240,12 @@
     LSA whose name matches key. This removal also causes the NPT to
     remove those name prefixes if no more LSAs advertise them.
    */
-  bool
+  void
   removeLsa(const ndn::Name& router, Lsa::Type lsaType);
 
+  void
+  removeLsa(const LsaContainer::index<Lsdb::byName>::type::iterator& lsaIt);
+
   /*! \brief Attempts to construct an adj. LSA.
 
     This function will attempt to construct an adjacency LSA. An LSA
@@ -260,10 +265,7 @@
     \param expTime How many seconds to wait before triggering the event.
    */
   ndn::scheduler::EventId
-  scheduleLsaExpiration(std::shared_ptr<Lsa> lsa, ndn::time::seconds expTime)
-  {
-    return m_scheduler.schedule(expTime + GRACE_PERIOD, [this, lsa] { expireOrRefreshLsa(lsa); });
-  }
+  scheduleLsaExpiration(std::shared_ptr<Lsa> lsa, ndn::time::seconds expTime);
 
   /*! \brief Either allow to expire, or refresh a name LSA.
     \param lsa The LSA.
@@ -321,16 +323,17 @@
   }
 
 public:
-  ndn::util::signal::Signal<Lsdb, Statistics::PacketType> lsaIncrementSignal;
-  ndn::util::signal::Signal<Lsdb, const ndn::Data&> afterSegmentValidatedSignal;
+  ndn::util::Signal<Lsdb, Statistics::PacketType> lsaIncrementSignal;
+  ndn::util::Signal<Lsdb, ndn::Data> afterSegmentValidatedSignal;
+  using AfterLsdbModified = ndn::util::Signal<Lsdb, std::shared_ptr<Lsa>, LsdbUpdate,
+                                              std::list<ndn::Name>, std::list<ndn::Name>>;
+  AfterLsdbModified onLsdbModified;
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   ndn::Face& m_face;
   ndn::Scheduler m_scheduler;
 
   ConfParameter& m_confParam;
-  NamePrefixTable& m_namePrefixTable;
-  RoutingTable& m_routingTable;
 
   SyncLogicHandler m_sync;
 
@@ -351,7 +354,7 @@
   std::set<std::shared_ptr<ndn::util::SegmentFetcher>> m_fetchers;
   psync::SegmentPublisher m_segmentPublisher;
 
-  bool m_isBuildAdjLsaSheduled;
+  bool m_isBuildAdjLsaScheduled;
   int64_t m_adjBuildCount;
   ndn::scheduler::ScopedEventId m_scheduledAdjLsaBuild;
 
diff --git a/src/nlsr.cpp b/src/nlsr.cpp
index b8cec28..cb24559 100644
--- a/src/nlsr.cpp
+++ b/src/nlsr.cpp
@@ -42,9 +42,10 @@
   , m_adjacencyList(confParam.getAdjacencyList())
   , m_namePrefixList(confParam.getNamePrefixList())
   , m_fib(m_face, m_scheduler, m_adjacencyList, m_confParam, keyChain)
-  , m_routingTable(m_scheduler, m_fib, m_lsdb, m_namePrefixTable, m_confParam)
-  , m_namePrefixTable(m_fib, m_routingTable, m_routingTable.afterRoutingChange)
-  , m_lsdb(m_face, keyChain, m_confParam, m_namePrefixTable, m_routingTable)
+  , m_lsdb(m_face, keyChain, m_confParam)
+  , m_routingTable(m_scheduler, m_lsdb, m_confParam)
+  , m_namePrefixTable(confParam.getRouterPrefix(), m_fib, m_routingTable,
+                      m_routingTable.afterRoutingChange, m_lsdb.onLsdbModified)
   , m_helloProtocol(m_face, keyChain, confParam, m_routingTable, m_lsdb)
   , m_onNewLsaConnection(m_lsdb.getSync().onNewLsa->connect(
       [this] (const ndn::Name& updateName, uint64_t sequenceNumber,
@@ -83,30 +84,29 @@
   m_faceMonitor.onNotification.connect(std::bind(&Nlsr::onFaceEventNotification, this, _1));
   m_faceMonitor.start();
 
-  // Do key initialization and registrations first, then initialize faces
-  // which will create routes to neighbor
-
-  setStrategies();
+  m_fib.setStrategy(m_confParam.getLsaPrefix(), Fib::MULTICAST_STRATEGY, 0);
+  m_fib.setStrategy(m_confParam.getSyncPrefix(), Fib::MULTICAST_STRATEGY, 0);
 
   NLSR_LOG_DEBUG("Default NLSR identity: " << m_confParam.getSigningInfo().getSignerName());
 
-  // Can be moved to Lsdb ctor
-  setLsaInterestFilter();
-
   // Add top-level prefixes: router and localhost prefix
   addDispatcherTopPrefix(ndn::Name(m_confParam.getRouterPrefix()).append("nlsr"));
   addDispatcherTopPrefix(LOCALHOST_PREFIX);
 
   enableIncomingFaceIdIndication();
 
-  registerLocalhostPrefix();
-  registerRouterPrefix();
-
   initializeFaces(std::bind(&Nlsr::processFaceDataset, this, _1),
                   std::bind(&Nlsr::onFaceDatasetFetchTimeout, this, _1, _2, 0));
 
-  // Could be moved into ctor, but unit tests need to be modified
-  initialize();
+  m_adjacencyList.writeLog();
+  NLSR_LOG_DEBUG(m_namePrefixList);
+
+  // Need to set direct neighbors' costs to 0 for hyperbolic routing
+  if (m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_ON) {
+    for (auto&& neighbor : m_adjacencyList.getAdjList()) {
+      neighbor.setLinkCost(0);
+    }
+  }
 }
 
 void
@@ -145,87 +145,29 @@
 }
 
 void
-Nlsr::registrationFailed(const ndn::Name& name)
-{
-  NLSR_LOG_ERROR("ERROR: Failed to register prefix " << name << " in local hub's daemon");
-  NDN_THROW(Error("Error: Prefix registration failed"));
-}
-
-void
-Nlsr::onRegistrationSuccess(const ndn::Name& name)
-{
-  NLSR_LOG_DEBUG("Successfully registered prefix: " << name);
-}
-
-void
-Nlsr::setLsaInterestFilter()
-{
-  ndn::Name name = m_confParam.getLsaPrefix();
-
-  NLSR_LOG_DEBUG("Setting interest filter for LsaPrefix: " << name);
-
-  m_face.setInterestFilter(ndn::InterestFilter(name).allowLoopback(false),
-                           std::bind(&Lsdb::processInterest, &m_lsdb, _1, _2),
-                           std::bind(&Nlsr::onRegistrationSuccess, this, _1),
-                           std::bind(&Nlsr::registrationFailed, this, _1),
-                           m_confParam.getSigningInfo(), ndn::nfd::ROUTE_FLAG_CAPTURE);
-}
-
-void
 Nlsr::addDispatcherTopPrefix(const ndn::Name& topPrefix)
 {
+  registerPrefix(topPrefix);
   try {
     // false since we want to have control over the registration process
     m_dispatcher.addTopPrefix(topPrefix, false, m_confParam.getSigningInfo());
   }
   catch (const std::exception& e) {
-    NLSR_LOG_ERROR("Error setting top-level prefix in dispatcher: " << e.what() << "\n");
+    NLSR_LOG_ERROR("Error setting top-level prefix in dispatcher: " << e.what());
   }
 }
 
 void
-Nlsr::setStrategies()
+Nlsr::registerPrefix(const ndn::Name& prefix)
 {
-  m_fib.setStrategy(m_confParam.getLsaPrefix(), Fib::MULTICAST_STRATEGY, 0);
-  m_fib.setStrategy(m_confParam.getSyncPrefix(), Fib::MULTICAST_STRATEGY, 0);
-}
-
-void
-Nlsr::initialize()
-{
-  // Logging start
-  m_adjacencyList.writeLog();
-  NLSR_LOG_DEBUG(m_namePrefixList);
-
-  m_lsdb.buildAndInstallOwnNameLsa();
-
-  // Install coordinate LSAs if using HR or dry-run HR.
-  if (m_confParam.getHyperbolicState() != HYPERBOLIC_STATE_OFF) {
-    m_lsdb.buildAndInstallOwnCoordinateLsa();
-  }
-
-  // Need to set direct neighbors' costs to 0 for hyperbolic routing
-  if (m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_ON) {
-    for (auto&& neighbor : m_adjacencyList.getAdjList()) {
-      neighbor.setLinkCost(0);
-    }
-  }
-}
-
-void
-Nlsr::registerLocalhostPrefix()
-{
-  m_face.registerPrefix(LOCALHOST_PREFIX,
-                        std::bind(&Nlsr::onRegistrationSuccess, this, _1),
-                        std::bind(&Nlsr::registrationFailed, this, _1));
-}
-
-void
-Nlsr::registerRouterPrefix()
-{
-  m_face.registerPrefix(ndn::Name(m_confParam.getRouterPrefix()).append("nlsr"),
-                        std::bind(&Nlsr::onRegistrationSuccess, this, _1),
-                        std::bind(&Nlsr::registrationFailed, this, _1));
+  m_face.registerPrefix(prefix,
+    [] (const ndn::Name& name) {
+      NLSR_LOG_DEBUG("Successfully registered prefix: " << name);
+    },
+    [] (const ndn::Name& name, const std::string& reason) {
+      NLSR_LOG_ERROR("ERROR: Failed to register prefix " << name << " in local hub's daemon");
+      NDN_THROW(Error("Error: Prefix registration failed: " + reason));
+    });
 }
 
 void
@@ -319,9 +261,7 @@
                       const FetchDatasetTimeoutCallback& onFetchFailure)
 {
   NLSR_LOG_TRACE("Initializing Faces...");
-
   m_faceDatasetController.fetch<ndn::nfd::FaceDataset>(onFetchSuccess, onFetchFailure);
-
 }
 
 void
@@ -359,8 +299,7 @@
 }
 
 void
-Nlsr::registerAdjacencyPrefixes(const Adjacent& adj,
-                                const ndn::time::milliseconds& timeout)
+Nlsr::registerAdjacencyPrefixes(const Adjacent& adj, ndn::time::milliseconds timeout)
 {
   ndn::FaceUri faceUri = adj.getFaceUri();
   double linkCost = adj.getLinkCost();
@@ -423,22 +362,14 @@
   m_controller.start<ndn::nfd::FaceUpdateCommand>(
     ndn::nfd::ControlParameters()
       .setFlagBit(ndn::nfd::FaceFlagBit::BIT_LOCAL_FIELDS_ENABLED, true),
-    std::bind(&Nlsr::onFaceIdIndicationSuccess, this, _1),
-    std::bind(&Nlsr::onFaceIdIndicationFailure, this, _1));
-}
-
-void
-Nlsr::onFaceIdIndicationSuccess(const ndn::nfd::ControlParameters& cp)
-{
-  NLSR_LOG_DEBUG("Successfully enabled incoming face id indication"
-                 << "for face id " << cp.getFaceId());
-}
-
-void
-Nlsr::onFaceIdIndicationFailure(const ndn::nfd::ControlResponse& cr)
-{
-  NLSR_LOG_DEBUG("Failed to enable incoming face id indication feature: " <<
-                 "(code: " << cr.getCode() << ", reason: " << cr.getText() << ")");
+    [] (const ndn::nfd::ControlParameters& cp) {
+      NLSR_LOG_DEBUG("Successfully enabled incoming face id indication"
+                     << "for face id " << cp.getFaceId());
+    },
+    [] (const ndn::nfd::ControlResponse& cr) {
+      NLSR_LOG_WARN("Failed to enable incoming face id indication feature: " <<
+                    "(code: " << cr.getCode() << ", reason: " << cr.getText() << ")");
+    });
 }
 
 } // namespace nlsr
diff --git a/src/nlsr.hpp b/src/nlsr.hpp
index 23d73c2..3e22874 100644
--- a/src/nlsr.hpp
+++ b/src/nlsr.hpp
@@ -40,16 +40,11 @@
 
 #include <ndn-cxx/face.hpp>
 #include <ndn-cxx/security/key-chain.hpp>
-#include <ndn-cxx/security/certificate-fetcher-direct-fetch.hpp>
-#include <ndn-cxx/security/signing-helpers.hpp>
-#include <ndn-cxx/security/signing-info.hpp>
 #include <ndn-cxx/util/scheduler.hpp>
 #include <ndn-cxx/mgmt/nfd/face-event-notification.hpp>
 #include <ndn-cxx/mgmt/nfd/face-monitor.hpp>
 #include <ndn-cxx/mgmt/dispatcher.hpp>
 #include <ndn-cxx/mgmt/nfd/face-status.hpp>
-#include <ndn-cxx/data.hpp>
-#include <ndn-cxx/encoding/block.hpp>
 #include <ndn-cxx/encoding/nfd-constants.hpp>
 #include <ndn-cxx/mgmt/nfd/control-parameters.hpp>
 #include <ndn-cxx/mgmt/nfd/control-response.hpp>
@@ -70,26 +65,6 @@
 
   Nlsr(ndn::Face& face, ndn::KeyChain& keyChain, ConfParameter& confParam);
 
-  void
-  registerStrategyForCerts(const ndn::Name& originRouter);
-
-  void
-  registrationFailed(const ndn::Name& name);
-
-  void
-  onRegistrationSuccess(const ndn::Name& name);
-
-  void
-  setLsaInterestFilter();
-
-  /*! \brief Add top level prefixes for Dispatcher
-   *
-   * All dispatcher-related sub-prefixes *must* be registered before sub-prefixes
-   * must be added before adding top
-   */
-  void
-  addDispatcherTopPrefix(const ndn::Name& topPrefix);
-
   Lsdb&
   getLsdb()
   {
@@ -102,9 +77,19 @@
     return m_fib;
   }
 
+private:
   void
-  initialize();
+  registerStrategyForCerts(const ndn::Name& originRouter);
 
+  /*! \brief Add top level prefixes for Dispatcher
+   *
+   * All dispatcher-related sub-prefixes *must* be registered before sub-prefixes
+   * must be added before adding top
+   */
+  void
+  addDispatcherTopPrefix(const ndn::Name& topPrefix);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   /*! \brief Initializes neighbors' Faces using information from NFD.
    * \sa Nlsr::initialize()
    * \sa Nlsr::processFaceDataset()
@@ -137,6 +122,7 @@
   void
   processFaceDataset(const std::vector<ndn::nfd::FaceStatus>& faces);
 
+private:
   /*! \brief Registers NLSR-specific prefixes for a neighbor (Adjacent)
    * \sa Nlsr::initializeFaces
    * \param adj A reference to the neighbor to register prefixes for
@@ -147,22 +133,12 @@
    * *each* registration request that is made.
    */
   void
-  registerAdjacencyPrefixes(const Adjacent& adj,
-                            const ndn::time::milliseconds& timeout);
+  registerAdjacencyPrefixes(const Adjacent& adj, ndn::time::milliseconds timeout);
 
-  void
-  setStrategies();
-
-private:
-  /*! \brief Registers the prefix that NLSR will consider to be the machine-local, secure prefix.
+  /*! \brief Registers the prefix
    */
   void
-  registerLocalhostPrefix();
-
-  /*! \brief Registers the <router-prefix>/nlsr so that NLSR can respond to status requests from remote routers.
-   */
-  void
-  registerRouterPrefix();
+  registerPrefix(const ndn::Name& prefix);
 
   /*! \brief Do nothing.
    */
@@ -182,12 +158,6 @@
   void
   enableIncomingFaceIdIndication();
 
-  void
-  onFaceIdIndicationSuccess(const ndn::nfd::ControlParameters& cp);
-
-  void
-  onFaceIdIndicationFailure(const ndn::nfd::ControlResponse& cr);
-
 public:
   static const ndn::Name LOCALHOST_PREFIX;
 
@@ -201,9 +171,9 @@
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   Fib m_fib;
+  Lsdb m_lsdb;
   RoutingTable m_routingTable;
   NamePrefixTable m_namePrefixTable;
-  Lsdb m_lsdb;
   HelloProtocol m_helloProtocol;
 
 private:
diff --git a/src/publisher/dataset-interest-handler.cpp b/src/publisher/dataset-interest-handler.cpp
index 373b62b..46b1ab8 100644
--- a/src/publisher/dataset-interest-handler.cpp
+++ b/src/publisher/dataset-interest-handler.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2020,  The University of Memphis,
+ * Copyright (c) 2014-2021,  The University of Memphis,
  *                           Regents of the University of California,
  *                           Arizona Board of Regents.
  *
@@ -38,16 +38,9 @@
 DatasetInterestHandler::DatasetInterestHandler(ndn::mgmt::Dispatcher& dispatcher,
                                                const Lsdb& lsdb,
                                                const RoutingTable& rt)
-  : m_dispatcher(dispatcher)
-  , m_lsdb(lsdb)
+  : m_lsdb(lsdb)
   , m_routingTable(rt)
 {
-  setDispatcher(m_dispatcher);
-}
-
-void
-DatasetInterestHandler::setDispatcher(ndn::mgmt::Dispatcher& dispatcher)
-{
   dispatcher.addStatusDataset(ADJACENCIES_DATASET,
     ndn::mgmt::makeAcceptAllAuthorization(),
     std::bind(&DatasetInterestHandler::publishLsaStatus<AdjLsa>, this, _1, _2, _3));
@@ -67,6 +60,7 @@
 DatasetInterestHandler::publishLsaStatus(const ndn::Name& topPrefix, const ndn::Interest& interest,
                                          ndn::mgmt::StatusDatasetContext& context)
 {
+  NLSR_LOG_TRACE("Received interest: " << interest);
   auto lsaRange = m_lsdb.getLsdbIterator<T>();
   for (auto lsaIt = lsaRange.first; lsaIt != lsaRange.second; ++lsaIt) {
     context.append((*lsaIt)->wireEncode());
@@ -78,7 +72,7 @@
 DatasetInterestHandler::publishRtStatus(const ndn::Name& topPrefix, const ndn::Interest& interest,
                                         ndn::mgmt::StatusDatasetContext& context)
 {
-  NLSR_LOG_DEBUG("Received interest: " << interest);
+  NLSR_LOG_TRACE("Received interest: " << interest);
   context.append(m_routingTable.wireEncode());
   context.end();
 }
diff --git a/src/publisher/dataset-interest-handler.hpp b/src/publisher/dataset-interest-handler.hpp
index cfd21c9..9e5caaf 100644
--- a/src/publisher/dataset-interest-handler.hpp
+++ b/src/publisher/dataset-interest-handler.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2020,  The University of Memphis,
+ * Copyright (c) 2014-2021,  The University of Memphis,
  *                           Regents of the University of California,
  *                           Arizona Board of Regents.
  *
@@ -71,11 +71,6 @@
                          const RoutingTable& rt);
 
 private:
-  /*! \brief set dispatcher for localhost or remote router
-   */
-  void
-  setDispatcher(ndn::mgmt::Dispatcher& dispatcher);
-
   /*! \brief provide routing-table dataset
   */
   void
@@ -90,7 +85,6 @@
                    ndn::mgmt::StatusDatasetContext& context);
 
 private:
-  ndn::mgmt::Dispatcher& m_dispatcher;
   const Lsdb& m_lsdb;
   const RoutingTable& m_routingTable;
 };
diff --git a/src/route/name-prefix-table.cpp b/src/route/name-prefix-table.cpp
index 91e6210..deb9af7 100644
--- a/src/route/name-prefix-table.cpp
+++ b/src/route/name-prefix-table.cpp
@@ -33,26 +33,88 @@
 
 INIT_LOGGER(route.NamePrefixTable);
 
-NamePrefixTable::NamePrefixTable(Fib& fib, RoutingTable& routingTable,
-                                 std::unique_ptr<AfterRoutingChange>& afterRoutingChangeSignal)
-  : m_fib(fib)
+NamePrefixTable::NamePrefixTable(const ndn::Name& ownRouterName, Fib& fib,
+                                 RoutingTable& routingTable,
+                                 AfterRoutingChange& afterRoutingChangeSignal,
+                                 Lsdb::AfterLsdbModified& afterLsdbModifiedSignal)
+  : m_ownRouterName(ownRouterName)
+  , m_fib(fib)
   , m_routingTable(routingTable)
 {
-  m_afterRoutingChangeConnection = afterRoutingChangeSignal->connect(
+  m_afterRoutingChangeConnection = afterRoutingChangeSignal.connect(
     [this] (const std::list<RoutingTableEntry>& entries) {
       updateWithNewRoute(entries);
     });
+
+  m_afterLsdbModified = afterLsdbModifiedSignal.connect(
+    [this] (std::shared_ptr<Lsa> lsa, LsdbUpdate updateType,
+            const auto& namesToAdd, const auto& namesToRemove) {
+      updateFromLsdb(lsa, updateType, namesToAdd, namesToRemove);
+    }
+  );
 }
 
 NamePrefixTable::~NamePrefixTable()
 {
   m_afterRoutingChangeConnection.disconnect();
+  m_afterLsdbModified.disconnect();
+}
+
+void
+NamePrefixTable::updateFromLsdb(std::shared_ptr<Lsa> lsa, LsdbUpdate updateType,
+                                const std::list<ndn::Name>& namesToAdd,
+                                const std::list<ndn::Name>& namesToRemove)
+{
+  if (m_ownRouterName == lsa->getOriginRouter()) {
+    return;
+  }
+  NLSR_LOG_TRACE("Got update from Lsdb for router: " << lsa->getOriginRouter());
+
+  if (updateType == LsdbUpdate::INSTALLED) {
+    addEntry(lsa->getOriginRouter(), lsa->getOriginRouter());
+
+    if (lsa->getType() == Lsa::Type::NAME) {
+      auto nlsa = std::static_pointer_cast<NameLsa>(lsa);
+      for (const auto& name : nlsa->getNpl().getNames()) {
+        if (name != m_ownRouterName) {
+          addEntry(name, lsa->getOriginRouter());
+        }
+      }
+    }
+  }
+  else if (updateType == LsdbUpdate::UPDATED) {
+    if (lsa->getType() != Lsa::Type::NAME) {
+      return;
+    }
+
+    for (const auto& name : namesToAdd) {
+      if (name != m_ownRouterName) {
+        addEntry(name, lsa->getOriginRouter());
+      }
+    }
+
+    for (const auto& name : namesToRemove) {
+      if (name != m_ownRouterName) {
+        removeEntry(name, lsa->getOriginRouter());
+      }
+    }
+  }
+  else {
+    removeEntry(lsa->getOriginRouter(), lsa->getOriginRouter());
+    if (lsa->getType() == Lsa::Type::NAME) {
+      auto nlsa = std::static_pointer_cast<NameLsa>(lsa);
+      for (const auto& name : nlsa->getNpl().getNames()) {
+        if (name != m_ownRouterName) {
+          removeEntry(name, lsa->getOriginRouter());
+        }
+      }
+    }
+  }
 }
 
 void
 NamePrefixTable::addEntry(const ndn::Name& name, const ndn::Name& destRouter)
 {
-
   // Check if the advertised name prefix is in the table already.
   NptEntryList::iterator nameItr =
     std::find_if(m_table.begin(),
diff --git a/src/route/name-prefix-table.hpp b/src/route/name-prefix-table.hpp
index 370e515..449b928 100644
--- a/src/route/name-prefix-table.hpp
+++ b/src/route/name-prefix-table.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2020,  The University of Memphis,
+/*
+ * Copyright (c) 2014-2021,  The University of Memphis,
  *                           Regents of the University of California,
  *                           Arizona Board of Regents.
  *
@@ -17,7 +17,7 @@
  *
  * You should have received a copy of the GNU General Public License along with
  * NLSR, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- **/
+ */
 
 #ifndef NLSR_NAME_PREFIX_TABLE_HPP
 #define NLSR_NAME_PREFIX_TABLE_HPP
@@ -27,6 +27,7 @@
 #include "signals.hpp"
 #include "test-access-control.hpp"
 #include "route/fib.hpp"
+#include "lsdb.hpp"
 
 #include <list>
 #include <unordered_map>
@@ -41,11 +42,23 @@
   using NptEntryList = std::list<std::shared_ptr<NamePrefixTableEntry>>;
   using const_iterator = NptEntryList::const_iterator;
 
-  NamePrefixTable(Fib& fib, RoutingTable& routingTable,
-                  std::unique_ptr<AfterRoutingChange>& afterRoutingChangeSignal);
+  NamePrefixTable(const ndn::Name& ownRouterName, Fib& fib, RoutingTable& routingTable,
+                  AfterRoutingChange& afterRoutingChangeSignal,
+                  Lsdb::AfterLsdbModified& afterLsdbModifiedSignal);
 
   ~NamePrefixTable();
 
+  /*! \brief Add, update, or remove Names according to the Lsdb update
+    \param lsa The LSA class pointer
+    \param updateType Update type from Lsdb (INSTALLED, UPDATED, REMOVED)
+    \param namesToAdd If LSA is UPDATED, then these are the names to add
+    \param namesToRemove If LSA is UPDATED, then these are the names to remove
+   */
+  void
+  updateFromLsdb(std::shared_ptr<Lsa> lsa, LsdbUpdate updateType,
+                 const std::list<ndn::Name>& namesToAdd,
+                 const std::list<ndn::Name>& namesToRemove);
+
   /*! \brief Adds a destination to the specified name prefix.
     \param name The name prefix
     \param destRouter The destination router prefix
@@ -125,9 +138,11 @@
   NptEntryList m_table;
 
 private:
+  const ndn::Name& m_ownRouterName;
   Fib& m_fib;
   RoutingTable& m_routingTable;
   ndn::util::signal::Connection m_afterRoutingChangeConnection;
+  ndn::util::signal::Connection m_afterLsdbModified;
 };
 
 inline NamePrefixTable::const_iterator
diff --git a/src/route/routing-table.cpp b/src/route/routing-table.cpp
index b600882..c06b429 100644
--- a/src/route/routing-table.cpp
+++ b/src/route/routing-table.cpp
@@ -24,93 +24,85 @@
 #include "conf-parameter.hpp"
 #include "routing-table-calculator.hpp"
 #include "routing-table-entry.hpp"
-#include "name-prefix-table.hpp"
 #include "logger.hpp"
 #include "tlv-nlsr.hpp"
 
-#include <list>
-#include <string>
-
 namespace nlsr {
 
 INIT_LOGGER(route.RoutingTable);
 
-RoutingTable::RoutingTable(ndn::Scheduler& scheduler, Fib& fib, Lsdb& lsdb,
-                           NamePrefixTable& namePrefixTable, ConfParameter& confParam)
-  : afterRoutingChange{std::make_unique<AfterRoutingChange>()}
-  , m_scheduler(scheduler)
-  , m_fib(fib)
+RoutingTable::RoutingTable(ndn::Scheduler& scheduler, Lsdb& lsdb, ConfParameter& confParam)
+  : m_scheduler(scheduler)
   , m_lsdb(lsdb)
-  , m_namePrefixTable(namePrefixTable)
   , m_routingCalcInterval{confParam.getRoutingCalcInterval()}
   , m_isRoutingTableCalculating(false)
   , m_isRouteCalculationScheduled(false)
   , m_confParam(confParam)
+  , m_hyperbolicState(m_confParam.getHyperbolicState())
 {
+  m_afterLsdbModified = lsdb.onLsdbModified.connect(
+    [this] (std::shared_ptr<Lsa> lsa, LsdbUpdate updateType,
+            const auto& namesToAdd, const auto& namesToRemove) {
+      auto type = lsa->getType();
+      bool updateForOwnAdjacencyLsa = lsa->getOriginRouter() == m_confParam.getRouterPrefix() &&
+                                      type == Lsa::Type::ADJACENCY;
+      bool scheduleCalculation = false;
+
+      if (updateType == LsdbUpdate::REMOVED && updateForOwnAdjacencyLsa) {
+        // If own Adjacency LSA is removed then we have no ACTIVE neighbors.
+        // (Own Coordinate LSA is never removed. But routing table calculation is scheduled
+        // in HelloProtocol. The routing table calculator for HR takes into account
+        // the INACTIVE status of the link).
+        NLSR_LOG_DEBUG("No Adj LSA of router itself, routing table can not be calculated :(");
+        clearRoutingTable();
+        clearDryRoutingTable();
+        NLSR_LOG_DEBUG("Calling Update NPT With new Route");
+        afterRoutingChange(m_rTable);
+        NLSR_LOG_DEBUG(*this);
+        m_ownAdjLsaExist = false;
+      }
+
+      if (updateType == LsdbUpdate::INSTALLED && updateForOwnAdjacencyLsa) {
+        m_ownAdjLsaExist = true;
+      }
+
+      // Don;t do anything on removal, wait for HelloProtocol to confirm and then react
+      if (updateType == LsdbUpdate::INSTALLED || updateType == LsdbUpdate::UPDATED) {
+        if ((type == Lsa::Type::ADJACENCY  && m_hyperbolicState != HYPERBOLIC_STATE_ON) ||
+            (type == Lsa::Type::COORDINATE && m_hyperbolicState != HYPERBOLIC_STATE_OFF)) {
+          scheduleCalculation = true;
+        }
+      }
+
+      if (scheduleCalculation) {
+        scheduleRoutingTableCalculation();
+      }
+    }
+  );
 }
 
 void
 RoutingTable::calculate()
 {
   m_lsdb.writeLog();
-  m_namePrefixTable.writeLog();
+  NLSR_LOG_TRACE("Calculating routing table");
+
   if (m_isRoutingTableCalculating == false) {
-    // setting routing table calculation
     m_isRoutingTableCalculating = true;
 
-    bool isHrEnabled = m_confParam.getHyperbolicState() != HYPERBOLIC_STATE_OFF;
-
-    if ((!isHrEnabled &&
-         m_lsdb.doesLsaExist(m_confParam.getRouterPrefix(), Lsa::Type::ADJACENCY))
-        ||
-        (isHrEnabled &&
-         m_lsdb.doesLsaExist(m_confParam.getRouterPrefix(), Lsa::Type::COORDINATE))) {
-      if (m_lsdb.getIsBuildAdjLsaSheduled() != 1) {
-        NLSR_LOG_TRACE("Clearing old routing table");
-        clearRoutingTable();
-        // for dry run options
-        clearDryRoutingTable();
-
-        NLSR_LOG_DEBUG("Calculating routing table");
-
-        // calculate Link State routing
-        if ((m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_OFF) ||
-            (m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_DRY_RUN)) {
-          calculateLsRoutingTable();
-        }
-        // calculate hyperbolic routing
-        if (m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_ON) {
-          calculateHypRoutingTable(false);
-        }
-        // calculate dry hyperbolic routing
-        if (m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_DRY_RUN) {
-          calculateHypRoutingTable(true);
-        }
-        // Inform the NPT that updates have been made
-        NLSR_LOG_DEBUG("Calling Update NPT With new Route");
-        (*afterRoutingChange)(m_rTable);
-        NLSR_LOG_DEBUG(*this);
-        m_namePrefixTable.writeLog();
-        m_fib.writeLog();
-      }
-      else {
-        NLSR_LOG_DEBUG("Adjacency building is scheduled, so routing table can not be calculated :(");
-      }
+    if (m_hyperbolicState == HYPERBOLIC_STATE_OFF) {
+      calculateLsRoutingTable();
     }
-    else {
-      NLSR_LOG_DEBUG("No Adj LSA of router itself, so Routing table can not be calculated :(");
-      clearRoutingTable();
-      clearDryRoutingTable(); // for dry run options
-      // need to update NPT here
-      NLSR_LOG_DEBUG("Calling Update NPT With new Route");
-      (*afterRoutingChange)(m_rTable);
-      NLSR_LOG_DEBUG(*this);
-      m_namePrefixTable.writeLog();
-      m_fib.writeLog();
-      // debugging purpose end
+    else if (m_hyperbolicState == HYPERBOLIC_STATE_DRY_RUN) {
+      calculateLsRoutingTable();
+      calculateHypRoutingTable(true);
     }
-    m_isRouteCalculationScheduled = false; // clear scheduled flag
-    m_isRoutingTableCalculating = false; // unsetting routing table calculation
+    else if (m_hyperbolicState == HYPERBOLIC_STATE_ON) {
+      calculateHypRoutingTable(false);
+    }
+
+    m_isRouteCalculationScheduled = false;
+    m_isRoutingTableCalculating = false;
   }
   else {
     scheduleRoutingTableCalculation();
@@ -122,6 +114,19 @@
 {
   NLSR_LOG_TRACE("CalculateLsRoutingTable Called");
 
+  if (m_lsdb.getIsBuildAdjLsaScheduled()) {
+    NLSR_LOG_DEBUG("Adjacency build is scheduled, routing table can not be calculated :(");
+    return;
+  }
+
+  // We only check this in LS since we never remove our own Coordinate LSA,
+  // whereas we remove our own Adjacency LSA if we don't have any neighbors
+  if (!m_ownAdjLsaExist) {
+    return;
+  }
+
+  clearRoutingTable();
+
   Map map;
   auto lsaRange = m_lsdb.getLsdbIterator<AdjLsa>();
   map.createFromAdjLsdb(lsaRange.first, lsaRange.second);
@@ -132,11 +137,22 @@
   LinkStateRoutingTableCalculator calculator(nRouters);
 
   calculator.calculatePath(map, *this, m_confParam, m_lsdb);
+
+  NLSR_LOG_DEBUG("Calling Update NPT With new Route");
+  afterRoutingChange(m_rTable);
+  NLSR_LOG_DEBUG(*this);
 }
 
 void
 RoutingTable::calculateHypRoutingTable(bool isDryRun)
 {
+  if (isDryRun) {
+    clearDryRoutingTable();
+  }
+  else {
+    clearRoutingTable();
+  }
+
   Map map;
   auto lsaRange = m_lsdb.getLsdbIterator<CoordinateLsa>();
   map.createFromCoordinateLsdb(lsaRange.first, lsaRange.second);
@@ -147,6 +163,12 @@
   HyperbolicRoutingCalculator calculator(nRouters, isDryRun, m_confParam.getRouterPrefix());
 
   calculator.calculatePath(map, *this, m_lsdb, m_confParam.getAdjacencyList());
+
+  if (!isDryRun) {
+    NLSR_LOG_DEBUG("Calling Update NPT With new Route");
+    afterRoutingChange(m_rTable);
+    NLSR_LOG_DEBUG(*this);
+  }
 }
 
 void
@@ -179,6 +201,7 @@
   else {
     rteChk->getNexthopList().addNextHop(nh);
   }
+  m_wire.reset();
 }
 
 RoutingTableEntry*
@@ -207,22 +230,21 @@
   else {
     it->getNexthopList().addNextHop(nh);
   }
+  m_wire.reset();
 }
 
 void
 RoutingTable::clearRoutingTable()
 {
-  if (m_rTable.size() > 0) {
-    m_rTable.clear();
-  }
+  m_rTable.clear();
+  m_wire.reset();
 }
 
 void
 RoutingTable::clearDryRoutingTable()
 {
-  if (m_dryTable.size() > 0) {
-    m_dryTable.clear();
-  }
+  m_dryTable.clear();
+  m_wire.reset();
 }
 
 template<ndn::encoding::Tag TAG>
diff --git a/src/route/routing-table.hpp b/src/route/routing-table.hpp
index 4a0ebd2..f7e0525 100644
--- a/src/route/routing-table.hpp
+++ b/src/route/routing-table.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2020,  The University of Memphis,
+ * Copyright (c) 2014-2021,  The University of Memphis,
  *                           Regents of the University of California
  *
  * This file is part of NLSR (Named-data Link State Routing).
@@ -27,6 +27,7 @@
 #include "lsdb.hpp"
 #include "route/fib.hpp"
 #include "test-access-control.hpp"
+#include "route/name-prefix-table.hpp"
 
 #include <ndn-cxx/util/scheduler.hpp>
 
@@ -89,8 +90,12 @@
 {
 public:
   explicit
-  RoutingTable(ndn::Scheduler& scheduler, Fib& fib, Lsdb& lsdb,
-               NamePrefixTable& namePrefixTable, ConfParameter& confParam);
+  RoutingTable(ndn::Scheduler& scheduler, Lsdb& lsdb, ConfParameter& confParam);
+
+  ~RoutingTable()
+  {
+    m_afterLsdbModified.disconnect();
+  }
 
   /*! \brief Calculates a list of next hops for each router in the network.
    *
@@ -122,24 +127,6 @@
   void
   scheduleRoutingTableCalculation();
 
-  void
-  setRoutingCalcInterval(uint32_t interval)
-  {
-    m_routingCalcInterval = ndn::time::seconds(interval);
-  }
-
-  const ndn::time::seconds&
-  getRoutingCalcInterval() const
-  {
-    return m_routingCalcInterval;
-  }
-
-  uint64_t
-  getRtSize()
-  {
-    return m_rTable.size();
-  }
-
 private:
   /*! \brief Calculates a link-state routing table. */
   void
@@ -156,21 +143,21 @@
   clearDryRoutingTable();
 
 public:
-  std::unique_ptr<AfterRoutingChange> afterRoutingChange;
+  AfterRoutingChange afterRoutingChange;
 
 private:
   ndn::Scheduler& m_scheduler;
-  Fib& m_fib;
   Lsdb& m_lsdb;
-  NamePrefixTable& m_namePrefixTable;
-
-  ndn::time::seconds m_routingCalcInterval;
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  ndn::time::seconds m_routingCalcInterval;
   bool m_isRoutingTableCalculating;
   bool m_isRouteCalculationScheduled;
 
   ConfParameter& m_confParam;
+  ndn::util::signal::Connection m_afterLsdbModified;
+  int32_t m_hyperbolicState;
+  bool m_ownAdjLsaExist = false;
 };
 
 } // namespace nlsr
diff --git a/src/security/certificate-store.cpp b/src/security/certificate-store.cpp
index 99067a7..1a3025e 100644
--- a/src/security/certificate-store.cpp
+++ b/src/security/certificate-store.cpp
@@ -33,11 +33,7 @@
 CertificateStore::CertificateStore(ndn::Face& face, ConfParameter& confParam, Lsdb& lsdb)
   : m_face(face)
   , m_confParam(confParam)
-  , m_lsdb(lsdb)
   , m_validator(m_confParam.getValidator())
-  , m_afterSegmentValidatedConnection(m_lsdb.afterSegmentValidatedSignal.connect(
-                                      std::bind(&CertificateStore::afterFetcherSignalEmitted,
-                                                this, _1)))
 {
   for (const auto& x: confParam.getIdCerts()) {
     auto idCert = ndn::io::load<ndn::security::Certificate>(x);
@@ -45,6 +41,9 @@
   }
 
   registerKeyPrefixes();
+
+  m_afterSegmentValidatedConnection = lsdb.afterSegmentValidatedSignal.connect(
+    [this] (const ndn::Data& data) { afterFetcherSignalEmitted(data); });
 }
 
 void
diff --git a/src/security/certificate-store.hpp b/src/security/certificate-store.hpp
index 95437c5..79a0cf7 100644
--- a/src/security/certificate-store.hpp
+++ b/src/security/certificate-store.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2020,  The University of Memphis,
+ * Copyright (c) 2014-2021,  The University of Memphis,
  *                           Regents of the University of California,
  *                           Arizona Board of Regents.
  *
@@ -96,7 +96,6 @@
   CertMap m_certificates;
   ndn::Face& m_face;
   ConfParameter& m_confParam;
-  Lsdb& m_lsdb;
   ndn::security::ValidatorConfig& m_validator;
   ndn::util::signal::ScopedConnection m_afterSegmentValidatedConnection;
 };