repo: Watching prefix
Change-Id: Ia315d6e529fa4402ad9ca4706b70db1e75eb054c
Refs: #1784
diff --git a/src/handles/base-handle.hpp b/src/handles/base-handle.hpp
index 9427356..44ac4a8 100644
--- a/src/handles/base-handle.hpp
+++ b/src/handles/base-handle.hpp
@@ -110,10 +110,10 @@
inline void
BaseHandle::reply(const Interest& commandInterest, const RepoCommandResponse& response)
{
- Data rdata(commandInterest.getName());
- rdata.setContent(response.wireEncode());
- m_keyChain.sign(rdata);
- m_face.put(rdata);
+ shared_ptr<Data> rdata = make_shared<Data>(commandInterest.getName());
+ rdata->setContent(response.wireEncode());
+ m_keyChain.sign(*rdata);
+ m_face.put(*rdata);
}
inline void
diff --git a/src/handles/watch-handle.cpp b/src/handles/watch-handle.cpp
new file mode 100644
index 0000000..15a82fa
--- /dev/null
+++ b/src/handles/watch-handle.cpp
@@ -0,0 +1,379 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014, Regents of the University of California.
+ *
+ * This file is part of NDN repo-ng (Next generation of NDN repository).
+ * See AUTHORS.md for complete list of repo-ng authors and contributors.
+ *
+ * repo-ng is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * repo-ng is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * repo-ng, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "watch-handle.hpp"
+
+namespace repo {
+
+static const milliseconds PROCESS_DELETE_TIME(10000);
+static const milliseconds DEFAULT_INTEREST_LIFETIME(4000);
+
+WatchHandle::WatchHandle(Face& face, RepoStorage& storageHandle, KeyChain& keyChain,
+ Scheduler& scheduler, ValidatorConfig& validator)
+ : BaseHandle(face, storageHandle, keyChain, scheduler)
+ , m_validator(validator)
+ , m_interestNum(0)
+ , m_maxInterestNum(0)
+ , m_interestLifetime(DEFAULT_INTEREST_LIFETIME)
+ , m_watchTimeout(0)
+ , m_startTime(steady_clock::now())
+ , m_size(0)
+{
+}
+
+void
+WatchHandle::deleteProcess(const Name& name)
+{
+ m_processes.erase(name);
+}
+
+// Interest.
+void
+WatchHandle::onInterest(const Name& prefix, const Interest& interest)
+{
+ m_validator.validate(interest,
+ bind(&WatchHandle::onValidated, this, _1, prefix),
+ bind(&WatchHandle::onValidationFailed, this, _1, _2));
+}
+
+void
+WatchHandle::onRegistered(const Name& prefix)
+{
+ getFace().setInterestFilter(Name().append(prefix).append("start"),
+ bind(&WatchHandle::onInterest, this, _1, _2));
+ getFace().setInterestFilter(Name().append(prefix).append("check"),
+ bind(&WatchHandle::onCheckInterest, this, _1, _2));
+ getFace().setInterestFilter(Name().append(prefix).append("stop"),
+ bind(&WatchHandle::onStopInterest, this, _1, _2));
+}
+
+// onRegisterFailed for watch start.
+void
+WatchHandle::onRegisterFailed(const Name& prefix, const std::string& reason)
+{
+ std::cerr << reason << std::endl;
+ throw Error("watch prefix registration failed");
+}
+
+void
+WatchHandle::onValidated(const shared_ptr<const Interest>& interest, const Name& prefix)
+{
+ RepoCommandParameter parameter;
+ try {
+ extractParameter(*interest, prefix, parameter);
+ }
+ catch (RepoCommandParameter::Error) {
+ negativeReply(*interest, 403);
+ return;
+ }
+
+ processWatchCommand(*interest, parameter);
+}
+
+void WatchHandle::watchStop(const Name& name)
+{
+ m_processes[name].second = false;
+ m_maxInterestNum = 0;
+ m_interestNum = 0;
+ m_startTime = steady_clock::now();
+ m_watchTimeout = milliseconds(0);
+ m_interestLifetime = DEFAULT_INTEREST_LIFETIME;
+ m_size = 0;
+}
+
+void
+WatchHandle::onValidationFailed(const shared_ptr<const Interest>& interest, const string& reason)
+{
+ std::cerr << reason << std::endl;
+ negativeReply(*interest, 401);
+}
+
+void
+WatchHandle::onData(const Interest& interest, ndn::Data& data, const Name& name)
+{
+ m_validator.validate(data,
+ bind(&WatchHandle::onDataValidated, this, interest, _1, name),
+ bind(&WatchHandle::onDataValidationFailed, this, interest, _1, _2, name));
+}
+
+void
+WatchHandle::onDataValidated(const Interest& interest, const shared_ptr<const Data>& data,
+ const Name& name)
+{
+ if (!m_processes[name].second) {
+ return;
+ }
+ if (getStorageHandle().insertData(*data)) {
+ m_size++;
+ if (!onRunning(name))
+ return;
+
+ Interest fetchInterest(interest.getName());
+ fetchInterest.setSelectors(interest.getSelectors());
+ fetchInterest.setInterestLifetime(m_interestLifetime);
+ fetchInterest.setChildSelector(1);
+
+ // update selectors
+ // if data name is equal to interest name, use MinSuffixComponents selecor to exclude this data
+ if (data->getName().size() == interest.getName().size()) {
+ fetchInterest.setMinSuffixComponents(2);
+ }
+ else {
+ Exclude exclude;
+ if (!interest.getExclude().empty()) {
+ exclude = interest.getExclude();
+ }
+
+ exclude.excludeBefore(data->getName()[interest.getName().size()]);
+ fetchInterest.setExclude(exclude);
+ }
+
+ ++m_interestNum;
+ getFace().expressInterest(fetchInterest,
+ bind(&WatchHandle::onData, this, _1, _2, name),
+ bind(&WatchHandle::onTimeout, this, _1, name));
+ }
+ else {
+ throw Error("Insert into Repo Failed");
+ }
+ m_processes[name].first.setInsertNum(m_size);
+}
+
+void
+WatchHandle::onDataValidationFailed(const Interest& interest, const shared_ptr<const Data>& data,
+ const std::string& reason, const Name& name)
+{
+ std::cerr << reason << std::endl;
+ if (!m_processes[name].second) {
+ return;
+ }
+ if (!onRunning(name))
+ return;
+
+ Interest fetchInterest(interest.getName());
+ fetchInterest.setSelectors(interest.getSelectors());
+ fetchInterest.setInterestLifetime(m_interestLifetime);
+ fetchInterest.setChildSelector(1);
+
+ // update selectors
+ // if data name is equal to interest name, use MinSuffixComponents selecor to exclude this data
+ if (data->getName().size() == interest.getName().size()) {
+ fetchInterest.setMinSuffixComponents(2);
+ }
+ else {
+ Exclude exclude;
+ if (!interest.getExclude().empty()) {
+ exclude = interest.getExclude();
+ }
+ // Only exclude this data since other data whose names are smaller may be validated and satisfied
+ exclude.excludeBefore(data->getName()[interest.getName().size()]);
+ fetchInterest.setExclude(exclude);
+ }
+
+ ++m_interestNum;
+ getFace().expressInterest(fetchInterest,
+ bind(&WatchHandle::onData, this, _1, _2, name),
+ bind(&WatchHandle::onTimeout, this, _1, name));
+}
+
+void
+WatchHandle::onTimeout(const ndn::Interest& interest, const Name& name)
+{
+ std::cerr << "Timeout" << std::endl;
+ if (!m_processes[name].second) {
+ return;
+ }
+ if (!onRunning(name))
+ return;
+ // selectors do not need to be updated
+ Interest fetchInterest(interest.getName());
+ fetchInterest.setSelectors(interest.getSelectors());
+ fetchInterest.setInterestLifetime(m_interestLifetime);
+ fetchInterest.setChildSelector(1);
+
+ ++m_interestNum;
+ getFace().expressInterest(fetchInterest,
+ bind(&WatchHandle::onData, this, _1, _2, name),
+ bind(&WatchHandle::onTimeout, this, _1, name));
+
+}
+
+void
+WatchHandle::listen(const Name& prefix)
+{
+ Name baseWatchPrefix(prefix);
+ baseWatchPrefix.append("watch");
+ getFace().registerPrefix(baseWatchPrefix,
+ bind(&WatchHandle::onRegistered, this, _1),
+ bind(&WatchHandle::onRegisterFailed, this, _1, _2));
+}
+
+void
+WatchHandle::onStopInterest(const Name& prefix, const Interest& interest)
+{
+ m_validator.validate(interest,
+ bind(&WatchHandle::onStopValidated, this, _1, prefix),
+ bind(&WatchHandle::onStopValidationFailed, this, _1, _2));
+}
+
+void
+WatchHandle::onStopValidated(const shared_ptr<const Interest>& interest, const Name& prefix)
+{
+ RepoCommandParameter parameter;
+ try {
+ extractParameter(*interest, prefix, parameter);
+ }
+ catch (RepoCommandParameter::Error) {
+ negativeReply(*interest, 403);
+ return;
+ }
+
+ watchStop(parameter.getName());
+ negativeReply(*interest, 101);
+}
+
+void
+WatchHandle::onStopValidationFailed(const shared_ptr<const Interest>& interest,
+ const std::string& reason)
+{
+ std::cerr << reason << std::endl;
+ negativeReply(*interest, 401);
+}
+
+void
+WatchHandle::onCheckInterest(const Name& prefix, const Interest& interest)
+{
+ m_validator.validate(interest,
+ bind(&WatchHandle::onCheckValidated, this, _1, prefix),
+ bind(&WatchHandle::onCheckValidationFailed, this, _1, _2));
+}
+
+void
+WatchHandle::onCheckValidated(const shared_ptr<const Interest>& interest, const Name& prefix)
+{
+ RepoCommandParameter parameter;
+ try {
+ extractParameter(*interest, prefix, parameter);
+ }
+ catch (RepoCommandParameter::Error) {
+ negativeReply(*interest, 403);
+ return;
+ }
+
+ if (!parameter.hasName()) {
+ negativeReply(*interest, 403);
+ return;
+ }
+ //check whether this process exists
+ Name name = parameter.getName();
+ if (m_processes.count(name) == 0) {
+ std::cerr << "no such process name: " << name << std::endl;
+ negativeReply(*interest, 404);
+ return;
+ }
+
+ RepoCommandResponse& response = m_processes[name].first;
+ if (!m_processes[name].second) {
+ response.setStatusCode(101);
+ }
+
+ reply(*interest, response);
+
+}
+
+void
+WatchHandle::onCheckValidationFailed(const shared_ptr<const Interest>& interest,
+ const std::string& reason)
+{
+ std::cerr << reason << std::endl;
+ negativeReply(*interest, 401);
+}
+
+void
+WatchHandle::deferredDeleteProcess(const Name& name)
+{
+ getScheduler().scheduleEvent(PROCESS_DELETE_TIME,
+ ndn::bind(&WatchHandle::deleteProcess, this, name));
+}
+
+void
+WatchHandle::processWatchCommand(const Interest& interest,
+ RepoCommandParameter& parameter)
+{
+ // if there is no watchTimeout specified, m_watchTimeout will be set as 0 and this handle will run forever
+ if (parameter.hasWatchTimeout()) {
+ m_watchTimeout = parameter.getWatchTimeout();
+ }
+ else {
+ m_watchTimeout = milliseconds(0);
+ }
+
+ // if there is no maxInterestNum specified, m_maxInterestNum will be 0, which means infinity
+ if (parameter.hasMaxInterestNum()) {
+ m_maxInterestNum = parameter.getMaxInterestNum();
+ }
+ else {
+ m_maxInterestNum = 0;
+ }
+
+ if (parameter.hasInterestLifetime()) {
+ m_interestLifetime = parameter.getInterestLifetime();
+ }
+
+ reply(interest, RepoCommandResponse().setStatusCode(100));
+
+ m_processes[parameter.getName()] =
+ std::make_pair(RepoCommandResponse().setStatusCode(300), true);
+ Interest fetchInterest(parameter.getName());
+ if (parameter.hasSelectors()) {
+ fetchInterest.setSelectors(parameter.getSelectors());
+ }
+ fetchInterest.setChildSelector(1);
+ fetchInterest.setInterestLifetime(m_interestLifetime);
+ m_startTime = steady_clock::now();
+ m_interestNum++;
+ getFace().expressInterest(fetchInterest,
+ bind(&WatchHandle::onData, this, _1, _2, parameter.getName()),
+ bind(&WatchHandle::onTimeout, this, _1, parameter.getName()));
+}
+
+
+void
+WatchHandle::negativeReply(const Interest& interest, int statusCode)
+{
+ RepoCommandResponse response;
+ response.setStatusCode(statusCode);
+ reply(interest, response);
+}
+
+bool
+WatchHandle::onRunning(const Name& name)
+{
+ bool isTimeout = (m_watchTimeout != milliseconds::zero() &&
+ steady_clock::now() - m_startTime > m_watchTimeout);
+ bool isMaxInterest = m_interestNum >= m_maxInterestNum && m_maxInterestNum != 0;
+ if (isTimeout || isMaxInterest) {
+ deferredDeleteProcess(name);
+ watchStop(name);
+ return false;
+ }
+ return true;
+}
+
+} //namespace repo
diff --git a/src/handles/watch-handle.hpp b/src/handles/watch-handle.hpp
new file mode 100644
index 0000000..10cddda
--- /dev/null
+++ b/src/handles/watch-handle.hpp
@@ -0,0 +1,170 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014, Regents of the University of California.
+ *
+ * This file is part of NDN repo-ng (Next generation of NDN repository).
+ * See AUTHORS.md for complete list of repo-ng authors and contributors.
+ *
+ * repo-ng is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * repo-ng is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * repo-ng, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef REPO_HANDLES_WATCH_HANDLE_HPP
+#define REPO_HANDLES_WATCH_HANDLE_HPP
+
+#include "base-handle.hpp"
+
+#include <queue>
+
+namespace repo {
+
+using std::map;
+using std::pair;
+using std::queue;
+using namespace ndn::time;
+/**
+ * @brief WatchHandle provides a different way for repo to insert data.
+ *
+ * Repo keeps sending interest to request the data with same prefix,
+ *
+ * but with different exclude selectors(updated every time). Repo will stop
+ *
+ * watching the prefix until a command interest tell it to stop, the total
+ *
+ * amount of sent interests reaches a specific number or time out.
+ */
+class WatchHandle : public BaseHandle
+{
+
+public:
+ class Error : public BaseHandle::Error
+ {
+ public:
+ explicit
+ Error(const std::string& what)
+ : BaseHandle::Error(what)
+ {
+ }
+ };
+
+
+public:
+ WatchHandle(Face& face, RepoStorage& storageHandle, KeyChain& keyChain,
+ Scheduler& scheduler, ValidatorConfig& validator);
+
+ virtual void
+ listen(const Name& prefix);
+
+private: // watch-insert command
+ /**
+ * @brief handle watch commands
+ */
+ void
+ onInterest(const Name& prefix, const Interest& interest);
+
+ void
+ onValidated(const shared_ptr<const Interest>& interest, const Name& prefix);
+
+ void
+ onValidationFailed(const shared_ptr<const Interest>& interest, const string& reason);
+
+ void
+ onRegistered(const Name& prefix);
+
+ void
+ onRegisterFailed(const Name& prefix, const std::string& reason);
+
+private: // data fetching
+ /**
+ * @brief fetch data and send next interest
+ */
+ void
+ onData(const Interest& interest, Data& data, const Name& name);
+
+ /**
+ * @brief handle when fetching one data timeout
+ */
+ void
+ onTimeout(const Interest& interest, const Name& name);
+
+ void
+ onDataValidated(const Interest& interest, const shared_ptr<const Data>& data,
+ const Name& name);
+
+ /**
+ * @brief failure of validation
+ */
+ void
+ onDataValidationFailed(const Interest& interest, const shared_ptr<const Data>& data,
+ const std::string& reason, const Name& name);
+
+
+ void
+ processWatchCommand(const Interest& interest, RepoCommandParameter& parameter);
+
+ void
+ watchStop(const Name& name);
+
+private: // watch state check command
+ /**
+ * @brief handle watch check command
+ */
+ void
+ onCheckInterest(const Name& prefix, const Interest& interest);
+
+ void
+ onCheckValidated(const shared_ptr<const Interest>& interest, const Name& prefix);
+
+ void
+ onCheckValidationFailed(const shared_ptr<const Interest>& interest, const std::string& reason);
+
+private: // watch stop command
+ /**
+ * @brief handle watch stop command
+ */
+ void
+ onStopInterest(const Name& prefix, const Interest& interest);
+
+ void
+ onStopValidated(const shared_ptr<const Interest>& interest, const Name& prefix);
+
+ void
+ onStopValidationFailed(const shared_ptr<const Interest>& interest, const std::string& reason);
+
+private:
+ void
+ negativeReply(const Interest& interest, int statusCode);
+
+ void
+ deferredDeleteProcess(const Name& name);
+
+ void
+ deleteProcess(const Name& name);
+
+ bool
+ onRunning(const Name& name);
+
+private:
+
+ ValidatorConfig& m_validator;
+
+ map<Name, std::pair<RepoCommandResponse, bool> > m_processes;
+ int64_t m_interestNum;
+ int64_t m_maxInterestNum;
+ milliseconds m_interestLifetime;
+ milliseconds m_watchTimeout;
+ ndn::time::steady_clock::TimePoint m_startTime;
+ int64_t m_size;
+};
+
+} // namespace repo
+
+#endif // REPO_HANDLES_Watch_HANDLE_HPP
diff --git a/src/handles/write-handle.cpp b/src/handles/write-handle.cpp
index 65098bf..0bc4e12 100644
--- a/src/handles/write-handle.cpp
+++ b/src/handles/write-handle.cpp
@@ -20,11 +20,13 @@
#include "write-handle.hpp"
namespace repo {
+using namespace ndn::time;
static const int RETRY_TIMEOUT = 3;
static const int DEFAULT_CREDIT = 12;
-static const ndn::time::milliseconds NOEND_TIMEOUT(10000);
-static const ndn::time::milliseconds PROCESS_DELETE_TIME(10000);
+static const milliseconds NOEND_TIMEOUT(10000);
+static const milliseconds PROCESS_DELETE_TIME(10000);
+static const milliseconds DEFAULT_INTEREST_LIFETIME(4000);
WriteHandle::WriteHandle(Face& face, RepoStorage& storageHandle, KeyChain& keyChain,
Scheduler& scheduler,// RepoStorage& storeindex,
@@ -34,6 +36,7 @@
, m_retryTime(RETRY_TIMEOUT)
, m_credit(DEFAULT_CREDIT)
, m_noEndTimeout(NOEND_TIMEOUT)
+ , m_interestLifetime(DEFAULT_INTEREST_LIFETIME)
{
}
@@ -91,7 +94,8 @@
else {
processSingleInsertCommand(*interest, parameter);
}
-
+ if (parameter.hasInterestLifetime())
+ m_interestLifetime = parameter.getInterestLifetime();
}
void
@@ -233,10 +237,12 @@
}
process.credit = initialCredit;
SegmentNo segment = startBlockId;
+
for (; segment < startBlockId + initialCredit; ++segment) {
Name fetchName = name;
fetchName.appendSegment(segment);
Interest interest(fetchName);
+ interest.setInterestLifetime(m_interestLifetime);
getFace().expressInterest(interest,
bind(&WriteHandle::onSegmentData, this, _1, _2, processId),
bind(&WriteHandle::onSegmentTimeout, this, _1, processId));
@@ -330,6 +336,7 @@
Name fetchName(interest.getName().getPrefix(-1));
fetchName.appendSegment(sendingSegment);
Interest fetchInterest(fetchName);
+ fetchInterest.setInterestLifetime(m_interestLifetime);
getFace().expressInterest(fetchInterest,
bind(&WriteHandle::onSegmentData, this, _1, _2, processId),
bind(&WriteHandle::onSegmentTimeout, this, _1, processId));
@@ -380,6 +387,7 @@
//Reput it in the queue, retryTime++
retryTime++;
Interest retryInterest(interest.getName());
+ retryInterest.setInterestLifetime(m_interestLifetime);
getFace().expressInterest(retryInterest,
bind(&WriteHandle::onSegmentData, this, _1, _2, processId),
bind(&WriteHandle::onSegmentTimeout, this, _1, processId));
@@ -475,6 +483,7 @@
response.setStatusCode(300);
Interest fetchInterest(parameter.getName());
+ fetchInterest.setInterestLifetime(m_interestLifetime);
if (parameter.hasSelectors()) {
fetchInterest.setSelectors(parameter.getSelectors());
}
diff --git a/src/handles/write-handle.hpp b/src/handles/write-handle.hpp
index 48bd0e4..bbbfe28 100644
--- a/src/handles/write-handle.hpp
+++ b/src/handles/write-handle.hpp
@@ -235,6 +235,7 @@
int m_retryTime;
int m_credit;
ndn::time::milliseconds m_noEndTimeout;
+ ndn::time::milliseconds m_interestLifetime;
};
} // namespace repo
diff --git a/src/repo-command-parameter.hpp b/src/repo-command-parameter.hpp
index 8a8c4bb..3c4cd54 100644
--- a/src/repo-command-parameter.hpp
+++ b/src/repo-command-parameter.hpp
@@ -33,6 +33,7 @@
using ndn::Selectors;
using ndn::EncodingEstimator;
using ndn::EncodingBuffer;
+using namespace ndn::time;
/**
* @brief Class defining abstraction of parameter of command for NDN Repo Protocol
@@ -57,6 +58,9 @@
, m_hasStartBlockId(false)
, m_hasEndBlockId(false)
, m_hasProcessId(false)
+ , m_hasMaxInterestNum(false)
+ , m_hasWatchTimeout(false)
+ , m_hasInterestLifetime(false)
{
}
@@ -158,12 +162,6 @@
return m_processId;
}
- bool
- hasProcessId() const
- {
- return m_hasProcessId;
- }
-
RepoCommandParameter&
setProcessId(uint64_t processId)
{
@@ -173,6 +171,78 @@
return *this;
}
+ bool
+ hasProcessId() const
+ {
+ return m_hasProcessId;
+ }
+
+ uint64_t
+ getMaxInterestNum() const
+ {
+ assert(hasMaxInterestNum());
+ return m_maxInterestNum;
+ }
+
+ RepoCommandParameter&
+ setMaxInterestNum(uint64_t maxInterestNum)
+ {
+ m_maxInterestNum = maxInterestNum;
+ m_hasMaxInterestNum = true;
+ m_wire.reset();
+ return *this;
+ }
+
+ bool
+ hasMaxInterestNum() const
+ {
+ return m_hasMaxInterestNum;
+ }
+
+ milliseconds
+ getWatchTimeout() const
+ {
+ assert(hasWatchTimeout());
+ return m_watchTimeout;
+ }
+
+ RepoCommandParameter&
+ setWatchTimeout(milliseconds watchTimeout)
+ {
+ m_watchTimeout = watchTimeout;
+ m_hasWatchTimeout = true;
+ m_wire.reset();
+ return *this;
+ }
+
+ bool
+ hasWatchTimeout() const
+ {
+ return m_hasWatchTimeout;
+ }
+
+ milliseconds
+ getInterestLifetime() const
+ {
+ assert(hasInterestLifetime());
+ return m_interestLifetime;
+ }
+
+ RepoCommandParameter&
+ setInterestLifetime(milliseconds interestLifetime)
+ {
+ m_interestLifetime = interestLifetime;
+ m_hasInterestLifetime = true;
+ m_wire.reset();
+ return *this;
+ }
+
+ bool
+ hasInterestLifetime() const
+ {
+ return m_hasInterestLifetime;
+ }
+
template<bool T>
size_t
wireEncode(EncodingImpl<T>& block) const;
@@ -190,11 +260,17 @@
uint64_t m_startBlockId;
uint64_t m_endBlockId;
uint64_t m_processId;
+ uint64_t m_maxInterestNum;
+ milliseconds m_watchTimeout;
+ milliseconds m_interestLifetime;
bool m_hasName;
bool m_hasStartBlockId;
bool m_hasEndBlockId;
bool m_hasProcessId;
+ bool m_hasMaxInterestNum;
+ bool m_hasWatchTimeout;
+ bool m_hasInterestLifetime;
mutable Block m_wire;
};
@@ -227,6 +303,27 @@
totalLength += encoder.prependVarNumber(tlv::StartBlockId);
}
+ if (m_hasMaxInterestNum) {
+ variableLength = encoder.prependNonNegativeInteger(m_maxInterestNum);
+ totalLength += variableLength;
+ totalLength += encoder.prependVarNumber(variableLength);
+ totalLength += encoder.prependVarNumber(tlv::MaxInterestNum);
+ }
+
+ if (m_hasWatchTimeout) {
+ variableLength = encoder.prependNonNegativeInteger(m_watchTimeout.count());
+ totalLength += variableLength;
+ totalLength += encoder.prependVarNumber(variableLength);
+ totalLength += encoder.prependVarNumber(tlv::WatchTimeout);
+ }
+
+ if (m_hasInterestLifetime) {
+ variableLength = encoder.prependNonNegativeInteger(m_interestLifetime.count());
+ totalLength += variableLength;
+ totalLength += encoder.prependVarNumber(variableLength);
+ totalLength += encoder.prependVarNumber(tlv::InterestLifetime);
+ }
+
if (!getSelectors().empty()) {
totalLength += getSelectors().wireEncode(encoder);
}
@@ -263,6 +360,9 @@
m_hasStartBlockId = false;
m_hasEndBlockId = false;
m_hasProcessId = false;
+ m_hasMaxInterestNum = false;
+ m_hasWatchTimeout = false;
+ m_hasInterestLifetime = false;
m_wire = wire;
@@ -312,6 +412,31 @@
m_processId = readNonNegativeInteger(*val);
}
+ // MaxInterestNum
+ val = m_wire.find(tlv::MaxInterestNum);
+ if (val != m_wire.elements_end())
+ {
+ m_hasMaxInterestNum = true;
+ m_maxInterestNum = readNonNegativeInteger(*val);
+ }
+
+ // WatchTimeout
+ val = m_wire.find(tlv::WatchTimeout);
+ if (val != m_wire.elements_end())
+ {
+ m_hasWatchTimeout = true;
+ m_watchTimeout = milliseconds(readNonNegativeInteger(*val));
+ }
+
+ // InterestLiftTime
+ val = m_wire.find(tlv::InterestLifetime);
+ if (val != m_wire.elements_end())
+ {
+ m_hasInterestLifetime = true;
+ m_interestLifetime = milliseconds(readNonNegativeInteger(*val));
+ }
+
+
}
inline std::ostream&
@@ -331,10 +456,22 @@
if (repoCommandParameter.hasEndBlockId()) {
os << " EndBlockId: " << repoCommandParameter.getEndBlockId();
}
- //ProcessId
+ // ProcessId
if (repoCommandParameter.hasProcessId()) {
os << " ProcessId: " << repoCommandParameter.getProcessId();
}
+ // MaxInterestNum
+ if (repoCommandParameter.hasMaxInterestNum()) {
+ os << " MaxInterestNum: " << repoCommandParameter.getMaxInterestNum();
+ }
+ // WatchTimeout
+ if (repoCommandParameter.hasProcessId()) {
+ os << " WatchTimeout: " << repoCommandParameter.getWatchTimeout();
+ }
+ // InterestLifetime
+ if (repoCommandParameter.hasProcessId()) {
+ os << " InterestLifetime: " << repoCommandParameter.getInterestLifetime();
+ }
os << " )";
return os;
}
diff --git a/src/repo-tlv.hpp b/src/repo-tlv.hpp
index 7d917e1..bdc3310 100644
--- a/src/repo-tlv.hpp
+++ b/src/repo-tlv.hpp
@@ -35,7 +35,9 @@
RepoCommandResponse = 207,
StatusCode = 208,
InsertNum = 209,
- DeleteNum = 210
+ DeleteNum = 210,
+ MaxInterestNum = 211,
+ WatchTimeout = 212
};
} // tlv
diff --git a/src/repo.cpp b/src/repo.cpp
index a75bdd5..346fa23 100644
--- a/src/repo.cpp
+++ b/src/repo.cpp
@@ -119,6 +119,7 @@
, m_validator(m_face)
, m_readHandle(m_face, m_storageHandle, m_keyChain, m_scheduler)
, m_writeHandle(m_face, m_storageHandle, m_keyChain, m_scheduler, m_validator)
+ , m_watchHandle(m_face, m_storageHandle, m_keyChain, m_scheduler, m_validator)
, m_deleteHandle(m_face, m_storageHandle, m_keyChain, m_scheduler, m_validator)
, m_tcpBulkInsertHandle(ioService, m_storageHandle)
@@ -143,6 +144,7 @@
++it)
{
m_writeHandle.listen(*it);
+ m_watchHandle.listen(*it);
m_deleteHandle.listen(*it);
}
diff --git a/src/repo.hpp b/src/repo.hpp
index abc25f4..ba518d1 100644
--- a/src/repo.hpp
+++ b/src/repo.hpp
@@ -26,6 +26,7 @@
#include "handles/read-handle.hpp"
#include "handles/write-handle.hpp"
+#include "handles/watch-handle.hpp"
#include "handles/delete-handle.hpp"
#include "handles/tcp-bulk-insert-handle.hpp"
@@ -87,6 +88,7 @@
ValidatorConfig m_validator;
ReadHandle m_readHandle;
WriteHandle m_writeHandle;
+ WatchHandle m_watchHandle;
DeleteHandle m_deleteHandle;
TcpBulkInsertHandle m_tcpBulkInsertHandle;
};
diff --git a/tests/integrated/test-basic-command-watch.cpp b/tests/integrated/test-basic-command-watch.cpp
new file mode 100644
index 0000000..ae06eb1
--- /dev/null
+++ b/tests/integrated/test-basic-command-watch.cpp
@@ -0,0 +1,267 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014, Regents of the University of California.
+ *
+ * This file is part of NDN repo-ng (Next generation of NDN repository).
+ * See AUTHORS.md for complete list of repo-ng authors and contributors.
+ *
+ * repo-ng is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * repo-ng is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * repo-ng, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "handles/watch-handle.hpp"
+#include "storage/sqlite-storage.hpp"
+#include "common.hpp"
+
+#include "../repo-storage-fixture.hpp"
+#include "../dataset-fixtures.hpp"
+
+#include <ndn-cxx/util/random.hpp>
+#include <ndn-cxx/util/io.hpp>
+
+#include <boost/test/unit_test.hpp>
+#include <fstream>
+
+namespace repo {
+namespace tests {
+
+using ndn::time::milliseconds;
+using ndn::time::seconds;
+using ndn::EventId;
+namespace random=ndn::random;
+
+//All the test cases in this test suite should be run at once.
+BOOST_AUTO_TEST_SUITE(TestBasicCommandWatchDelete)
+
+const static uint8_t content[8] = {3, 1, 4, 1, 5, 9, 2, 6};
+
+template<class Dataset>
+class Fixture : public RepoStorageFixture, public Dataset
+{
+public:
+ Fixture()
+ : scheduler(repoFace.getIoService())
+ , validator(repoFace)
+ , watchHandle(repoFace, *handle, keyChain, scheduler, validator)
+ , watchFace(repoFace.getIoService())
+ {
+ watchHandle.listen(Name("/repo/command"));
+ }
+
+ ~Fixture()
+ {
+ repoFace.getIoService().stop();
+ }
+
+ void
+ generateDefaultCertificateFile();
+
+ void
+ scheduleWatchEvent();
+
+ void
+ onWatchInterest(const Interest& interest);
+
+ void
+ onRegisterFailed(const std::string& reason);
+
+ void
+ delayedInterest();
+
+ void
+ stopFaceProcess();
+
+ void
+ onWatchData(const Interest& interest, Data& data);
+
+ void
+ onWatchStopData(const Interest& interest, Data& data);
+
+ void
+ onWatchTimeout(const Interest& interest);
+
+ void
+ sendWatchStartInterest(const Interest& interest);
+
+ void
+ sendWatchStopInterest(const Interest& interest);
+
+ void
+ checkWatchOk(const Interest& interest);
+
+public:
+ Face repoFace;
+ Scheduler scheduler;
+ ValidatorConfig validator;
+ KeyChain keyChain;
+ WatchHandle watchHandle;
+ Face watchFace;
+ std::map<Name, EventId> watchEvents;
+};
+
+template<class T> void
+Fixture<T>::generateDefaultCertificateFile()
+{
+ Name defaultIdentity = keyChain.getDefaultIdentity();
+ Name defaultKeyname = keyChain.getDefaultKeyNameForIdentity(defaultIdentity);
+ Name defaultCertficateName = keyChain.getDefaultCertificateNameForKey(defaultKeyname);
+ shared_ptr<ndn::IdentityCertificate> defaultCertficate =
+ keyChain.getCertificate(defaultCertficateName);
+ //test-integrated should run in root directory of repo-ng.
+ //certificate file should be removed after tests for security issue.
+ std::fstream certificateFile("tests/integrated/insert-delete-test.cert",
+ std::ios::out | std::ios::binary | std::ios::trunc);
+ ndn::io::save(*defaultCertficate, certificateFile);
+ certificateFile.close();
+}
+
+template<class T> void
+Fixture<T>::onWatchInterest(const Interest& interest)
+{
+ shared_ptr<Data> data = make_shared<Data>(Name(interest.getName()).appendNumber(random::generateWord64()+100));
+ data->setContent(content, sizeof(content));
+ data->setFreshnessPeriod(milliseconds(0));
+ keyChain.signByIdentity(*data, keyChain.getDefaultIdentity());
+ watchFace.put(*data);
+
+ // schedule an event 50ms later to check whether watch is Ok
+ scheduler.scheduleEvent(milliseconds(10000),
+ bind(&Fixture<T>::checkWatchOk, this,
+ Interest(data->getName())));
+}
+
+
+template<class T> void
+Fixture<T>::onRegisterFailed(const std::string& reason)
+{
+ BOOST_ERROR("ERROR: Failed to register prefix in local hub's daemon" + reason);
+}
+
+template<class T> void
+Fixture<T>::delayedInterest()
+{
+ BOOST_ERROR("Fetching interest does not come. It may be satisfied in CS or something is wrong");
+}
+
+template<class T> void
+Fixture<T>::stopFaceProcess()
+{
+ repoFace.getIoService().stop();
+}
+
+template<class T> void
+Fixture<T>::onWatchData(const Interest& interest, Data& data)
+{
+ RepoCommandResponse response;
+ response.wireDecode(data.getContent().blockFromValue());
+
+ int statusCode = response.getStatusCode();
+ BOOST_CHECK_EQUAL(statusCode, 100);
+}
+
+template<class T> void
+Fixture<T>::onWatchStopData(const Interest& interest, Data& data)
+{
+ RepoCommandResponse response;
+ response.wireDecode(data.getContent().blockFromValue());
+
+ int statusCode = response.getStatusCode();
+ BOOST_CHECK_EQUAL(statusCode, 101);
+}
+
+template<class T> void
+Fixture<T>::onWatchTimeout(const Interest& interest)
+{
+ BOOST_ERROR("Watch command timeout");
+}
+
+template<class T> void
+Fixture<T>::sendWatchStartInterest(const Interest& watchInterest)
+{
+ watchFace.expressInterest(watchInterest,
+ bind(&Fixture<T>::onWatchData, this, _1, _2),
+ bind(&Fixture<T>::onWatchTimeout, this, _1));
+}
+
+template<class T> void
+Fixture<T>::sendWatchStopInterest(const Interest& watchInterest)
+{
+ watchFace.expressInterest(watchInterest,
+ bind(&Fixture<T>::onWatchStopData, this, _1, _2),
+ bind(&Fixture<T>::onWatchTimeout, this, _1));
+}
+
+template<class T> void
+Fixture<T>::checkWatchOk(const Interest& interest)
+{
+ BOOST_TEST_MESSAGE(interest);
+ shared_ptr<Data> data = handle->readData(interest);
+ if (data) {
+ int rc = memcmp(data->getContent().value(), content, sizeof(content));
+ BOOST_CHECK_EQUAL(rc, 0);
+ }
+ else {
+ std::cerr<<"Check Watch Failed"<<std::endl;
+ }
+}
+
+template<class T> void
+Fixture<T>::scheduleWatchEvent()
+{
+ Name watchCommandName("/repo/command/watch/start");
+ RepoCommandParameter watchParameter;
+ watchParameter.setName(Name("/a/b"));
+ watchParameter.setMaxInterestNum(10);
+ watchParameter.setInterestLifetime(milliseconds(50000));
+ watchParameter.setWatchTimeout(milliseconds(1000000000));
+ watchCommandName.append(watchParameter.wireEncode());
+ Interest watchInterest(watchCommandName);
+ keyChain.signByIdentity(watchInterest, keyChain.getDefaultIdentity());
+ //schedule a job to express watchInterest
+ scheduler.scheduleEvent(milliseconds(1000),
+ bind(&Fixture<T>::sendWatchStartInterest, this, watchInterest));
+
+ Name watchStopName("/repo/command/watch/stop");
+ RepoCommandParameter watchStopParameter;
+ watchStopName.append(watchStopParameter.wireEncode());
+ Interest watchStopInterest(watchStopName);
+ keyChain.signByIdentity(watchStopInterest, keyChain.getDefaultIdentity());
+
+ // scheduler.scheduleEvent(milliseconds(10000),
+ // bind(&Fixture<T>::sendWatchStopInterest, this, watchStopInterest));
+ //The delayEvent will be canceled in onWatchInterest
+ watchFace.setInterestFilter(watchParameter.getName(),
+ bind(&Fixture<T>::onWatchInterest, this, _2),
+ ndn::RegisterPrefixSuccessCallback(),
+ bind(&Fixture<T>::onRegisterFailed, this, _2));
+}
+
+typedef boost::mpl::vector< BasicDataset > Dataset;
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(WatchDelete, T, Dataset, Fixture<T>)
+{
+ this->generateDefaultCertificateFile();
+ this->validator.load("tests/integrated/insert-delete-validator-config.conf");
+
+ // schedule events
+ this->scheduler.scheduleEvent(seconds(0),
+ bind(&Fixture<T>::scheduleWatchEvent, this));
+
+ // schedule an event to terminate IO
+ this->scheduler.scheduleEvent(seconds(500),
+ bind(&Fixture<T>::stopFaceProcess, this));
+ this->repoFace.getIoService().run();
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} //namespace tests
+} //namespace repo