servlib/prophet/BundleCore.h
changeset 0 2b3e5ec03512
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlib/prophet/BundleCore.h	Thu Apr 21 14:57:45 2011 +0100
@@ -0,0 +1,359 @@
+/*
+ *    Copyright 2007 Baylor University
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef _PROPHET_BUNDLE_CORE_FACADE_H_
+#define _PROPHET_BUNDLE_CORE_FACADE_H_
+
+#include "Alarm.h"
+#include "Node.h"
+#include "Bundle.h"
+#include "BundleImpl.h"
+#include "BundleList.h"
+#include "Link.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <string>
+#include <list>
+
+#if defined(__GNUC__)
+# define PRINTFLIKE(fmt, arg) __attribute__((format (printf, fmt, arg)))
+#else
+# define PRINTFLIKE(a, b)
+#endif
+
+namespace prophet
+{
+
+/**
+ * Prophet facade's abstract interface into DTN host's bundle core.
+ * Prophet requires Bundle handling (create, read, write, send, find), 
+ * routing functions, preferences and parameters, persistent storage,
+ * timers and timeout handling, and debug logging.  BundleCore decouples
+ * the Prophet facade library from its host implementation by abstracting
+ * away the specifics of implmentation for these core functions.
+ */
+class BundleCore
+{
+public:
+
+    /**
+     * Destructor
+     */
+    virtual ~BundleCore() {}
+
+    /**
+     * Answers whether dest_id matches route
+     */
+    virtual bool is_route(const std::string& dest_id,
+                          const std::string& route) const = 0;
+
+    /**
+     * Query the Bundle core for route status
+     */
+    virtual bool should_fwd(const Bundle* bundle,
+                            const Link* link) const = 0;
+
+    /**
+     * Given a Bundle's destination, return the route
+     */
+    virtual std::string get_route(const std::string& dest_id) const = 0;
+
+    /**
+     * Given a Bundle's destination, return the route pattern
+     */
+    virtual std::string get_route_pattern(const std::string& dest_id) const = 0;
+
+    /**
+     * Callback method for Prophet to query storage_quota()
+     */
+    virtual u_int64_t max_bundle_quota() const = 0;
+
+    /**
+     * Callback method for Prophet to query whether Bundle host
+     * is willing to accept custody transfers
+     */
+    virtual bool custody_accepted() const = 0;
+
+    /**
+     * Enumerate Bundles in host's store
+     */
+    virtual const BundleList& bundles() const = 0;
+
+    /**
+     * Callback method for Prophet to request for a bundle to be deleted;
+     * required by Repository's evict() routine
+     */
+    virtual void drop_bundle(const Bundle* bundle) = 0;
+
+    /**
+     * Given a Bundle and a candidate Link, attempt to send a Bundle
+     */
+    virtual bool send_bundle(const Bundle* bundle,
+                             const Link* link) = 0;
+
+    /**
+     * Transfer buffer into Bundle's payload
+     * @param bundle bundle metadata object
+     * @param buf Buffer to write into payload
+     * @return success
+     */
+    virtual bool write_bundle(const Bundle* bundle,
+                              const u_char* buf,
+                              size_t len) = 0;
+
+    /**
+     * Request a Bundle's payload from Bundle host. 
+     * @param bundle bundle metadata object
+     * @param buffer in/out memory into which to write Bundle payload
+     * @param len in/out size of inbound buffer, amount written into
+     *            outbound buffer
+     * @return success
+     */
+    virtual bool read_bundle(const Bundle* bundle,
+                             u_char* buffer,
+                             size_t& len) const = 0;
+
+    /**
+     * Factory method to request new Bundle from bundle host.
+     * @param src Route of bundle originator
+     * @param dst Route of bundle destination
+     * @param exp Lifespan in seconds
+     */
+    virtual Bundle* create_bundle(const std::string& src,
+                                  const std::string& dst,
+                                  u_int exp) = 0;
+
+    /**
+     * Given a BundleList, a route, a creation ts, and a sequence number,
+     * find the Bundle
+     */
+    virtual const Bundle* find(const BundleList& list, const std::string& eid,
+                         u_int32_t creation_ts, u_int32_t seqno) const = 0;
+
+    /**
+     * Update (or create) a persistent Node to reflect handle's info
+     */
+    virtual void update_node(const Node* node) = 0;
+
+    /**
+     * Remove this Node from persistent storage
+     */
+    virtual void delete_node(const Node* node) = 0;
+
+    /**
+     * Query the local endpoint ID
+     */
+    virtual std::string local_eid() const = 0;
+
+    /**
+     * Query for the endpoint ID to the Prophet node on the remote end of
+     * this link
+     */
+    virtual std::string prophet_id(const Link* link) const = 0;
+
+    /**
+     * Query for the endpoint ID to the local Prophet instance
+     */
+    virtual std::string prophet_id() const = 0;
+
+    /**
+     * Factory method to allocate memory for and return pointer to new
+     * Alarm object that will invoke handler->handle_timeout() after
+     * timeout milliseconds.
+     */
+    virtual Alarm* create_alarm(ExpirationHandler* handler,
+                                u_int timeout, bool jitter = false) = 0;
+
+    ///@{ Log level
+    static const int LOG_DEBUG   = 1;
+    static const int LOG_INFO    = 2;
+    static const int LOG_NOTICE  = 3;
+    static const int LOG_WARN    = 4;
+    static const int LOG_ERR     = 5;
+    static const int LOG_CRIT    = 6;
+    static const int LOG_ALWAYS  = 7;
+    ///@}
+
+    /**
+     * Defer implementation of logging to host system ... but still
+     * propagate messages up to host system from within prophet namespace
+     */
+    virtual void print_log(const char* name, int level, const char* fmt, ...)
+        PRINTFLIKE(4,5) = 0;
+
+}; // class BundleCore
+
+/**
+ * Mock object for use in testing
+ */
+class AlarmImpl : public Alarm
+{
+public:
+    AlarmImpl(ExpirationHandler* h)
+        : Alarm(h), pending_(false), cancelled_(false) {}
+
+    virtual ~AlarmImpl() {}
+
+    void schedule(u_int) { pending_ = true; }
+    u_int time_remaining() const { return 0; }
+    void cancel() { cancelled_ = true; }
+    bool pending() const { return pending_; }
+    bool cancelled() const { return cancelled_; }
+    bool pending_, cancelled_;
+}; // class AlarmImpl
+
+/**
+ * Mock object for use in unit testing; this doesn't really do 
+ * anything other than capture state for inspection by unit tests
+ */
+class BundleCoreTestImpl : public BundleCore
+{
+public:
+    typedef std::string BundleBuffer;
+    BundleCoreTestImpl(const std::string& str = "dtn://somehost")
+        : str_(str), max_(0xffff) {}
+    virtual ~BundleCoreTestImpl()
+    {
+        while (!alarms_.empty())
+        {
+            delete alarms_.front();
+            alarms_.pop_front();
+        }
+    }
+    ///@{ virtual from BundleCore
+    bool is_route(const std::string& dest,const std::string& route) const
+    {
+        if (route.length() > dest.length()) return false;
+        return route.compare(0,route.length(),dest) == 0;
+    }
+    bool should_fwd(const Bundle*,const Link*) const { return true; }
+    std::string get_route(const std::string& str ) const { return str; }
+    std::string get_route_pattern(const std::string& str ) const { return str + "/*"; }
+    u_int64_t max_bundle_quota() const { return max_; }
+    bool custody_accepted() const { return true; }
+    void drop_bundle(const Bundle* b)
+    {
+        for (std::list<bundle>::iterator i = rcvd_.begin();
+                i != rcvd_.end(); i++)
+        {
+            if (b->destination_id() == (*i).first->destination_id() &&
+                b->creation_ts() == (*i).first->creation_ts() &&
+                b->sequence_num() == (*i).first->sequence_num())
+            {
+                rcvd_.erase(i);
+                break;
+            }
+        }
+    }
+    bool send_bundle(const Bundle* b,const Link*)
+    {
+        sent_.push_back(b);
+        return true;
+    }
+    bool write_bundle(const Bundle* b,const u_char* buf,size_t len)
+    { 
+        BundleBuffer bunbuf((char*)buf,len);
+        written_.push_back(std::make_pair<const Bundle*,BundleBuffer>(b,bunbuf));
+        return written_.back().second.size() <= len;
+    }
+    bool read_bundle(const Bundle* b,u_char* buf,size_t& len) const
+    {
+        for (std::list<bundle>::const_iterator i = rcvd_.begin();
+                i != rcvd_.end(); i++)
+        {
+            if (b->destination_id() == (*i).first->destination_id() &&
+                b->creation_ts() == (*i).first->creation_ts() &&
+                b->sequence_num() == (*i).first->sequence_num())
+            {
+                size_t blen = (*i).second.size();
+                if (blen < len) return false;
+                len = blen;
+                memcpy(buf,(*i).second.data(),len);
+                return true;
+            }
+        }
+        return false;
+    }
+    Bundle* create_bundle(const std::string& src, const std::string& dst,u_int exp=3600)
+    { return new BundleImpl(src,dst,0,0,exp); }
+    const Bundle* find(const BundleList&,const std::string&,u_int32_t,
+            u_int32_t) const
+    { return NULL; }
+    const BundleList& bundles() const { return list_; }
+    void update_node(const Node*) {}
+    void delete_node(const Node*) {}
+    std::string local_eid() const { return str_; }
+#define PROPHESY(_str) do { \
+        size_t pos = _str.size() - 1; \
+        if (_str[pos] == '/') \
+            _str += "prophet"; \
+        else \
+            _str += "/prophet"; \
+    } while (0)
+    std::string prophet_id(const Link* link) const
+    {
+        remote_.assign(link->nexthop());
+        PROPHESY(remote_);
+        return remote_;
+    }
+    std::string prophet_id() const
+    {
+        if (local_ == "")
+        {
+            local_.assign(str_);
+            PROPHESY(local_);
+        }
+        return local_;
+    }
+#undef PROPHESY
+    Alarm* create_alarm(ExpirationHandler* handler, u_int timeout,bool)
+    {
+        AlarmImpl* alarm = new AlarmImpl(handler);
+        alarm->schedule(timeout);
+        alarms_.push_back(alarm);
+        return alarm;
+    }
+    void print_log(const char* name, int level, const char* fmt, ...)
+        PRINTFLIKE(4,5);
+
+    ///@}
+    void set_max(u_int64_t max) { max_ = max; }
+    void set_eid(const std::string& id) { str_.assign(id); }
+    std::string str_;
+    mutable std::string local_, remote_;
+    u_int64_t max_;
+    std::list<const Bundle*> sent_;
+    typedef std::pair<const Bundle*,BundleBuffer> bundle;
+    std::list<bundle> written_;
+    std::list<bundle> rcvd_;
+    std::list<Alarm*> alarms_;
+    prophet::BundleList list_;
+}; // class BundleCoreTestImpl
+
+inline void 
+BundleCoreTestImpl::print_log(const char* name, int level, const char* fmt, ...)
+{
+    printf("[%s][%d]\n",name,level);
+    va_list ap;
+    va_start(ap, fmt);
+    vprintf(fmt, ap);
+    va_end(ap);
+    printf("\n");
+}
+
+}; // namespace prophet
+
+#endif // _PROPHET_BUNDLE_CORE_FACADE_H_