servlib/conv_layers/ExternalConvergenceLayer.h
changeset 0 2b3e5ec03512
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlib/conv_layers/ExternalConvergenceLayer.h	Thu Apr 21 14:57:45 2011 +0100
@@ -0,0 +1,455 @@
+/* Copyright 2004-2006 BBN Technologies Corporation
+ *
+ * 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 _EXTERNAL_CONVERGENCE_LAYER_H_
+#define _EXTERNAL_CONVERGENCE_LAYER_H_
+
+#ifndef DTN_CONFIG_STATE
+#error "MUST INCLUDE dtn-config.h before including this file"
+#endif
+
+#if defined(XERCES_C_ENABLED) && defined(EXTERNAL_CL_ENABLED)
+
+#include <string>
+#include <list>
+
+/* Note that these classes are now deprecated, we'll need to rewrite */
+/* the code at some point to use the new standard classes. In the */
+/*  meantime, quiet the warnings. */
+/* undefine __DEPRECATED and remember it was set*/
+#ifdef __DEPRECATED
+# define __DEPRECATED_save
+# undef __DEPRECATED
+#endif
+
+#include <ext/hash_map>
+
+/* re-define __DEPRECATED if it was set */
+#ifdef __DEPRECATED_save
+# define __DEPRECATED
+#endif
+
+#include <oasys/thread/Thread.h>
+#include <oasys/thread/Mutex.h>
+#include <oasys/io/TCPServer.h>
+
+#include "ConvergenceLayer.h"
+#include "clevent.h"
+#include "bundling/BundleList.h"
+#include "bundling/BundleEvent.h"
+#include "contacts/NamedAttribute.h"
+
+namespace dtn {
+
+// g++ 4.3.x needs this unrolled, can't use a forward decl of the form
+//   class dtn::clmessage::bundle_attributes;
+namespace clmessage {
+    class bundle_attributes;
+    class link_config_parameters;
+};
+
+using __gnu_cxx::hash_multimap;
+using __gnu_cxx::hash;
+
+class Interface;
+class Contact;
+class ECLModule;
+
+typedef ::xsd::cxx::tree::sequence<clmessage::key_value_pair> KeyValueSequence;
+
+/** Base class for resources that should be owned by an ECLModule.
+ * 
+ * Classes derived from this class are used for keeping track of various
+ * resources (interfaces and links, for instance) that belong to a particular
+ * module.  This base class holds info about the owner module as well as a copy
+ * of the CLEvent that tells the external module to create the resource.
+ */
+class ECLResource : public CLInfo {
+public:
+    virtual ~ECLResource() {
+        delete create_message_;
+    }
+    
+    /// The protocol that this resource is intended for.
+    std::string protocol_;
+    
+    /// The CLEvent that will create this resource, in case we must send
+    /// it again.
+    clmessage::cl_message* create_message_;
+    
+    /// The module that owns this resource (this will be NULL if the resource
+    /// is unclaimed).
+    ECLModule* module_;
+    
+    oasys::Mutex lock_;
+    
+    bool should_delete_;
+    
+protected:
+    ECLResource(std::string p, clmessage::cl_message* create) :
+    lock_("ECLResource") {
+        protocol_ = p;
+        create_message_ = create;
+        module_ = NULL;
+        should_delete_ = true;
+    }
+};
+
+
+/** Represents a single Interface.
+ */
+class ECLInterfaceResource : public ECLResource {
+public:
+    ECLInterfaceResource(std::string p, clmessage::cl_message* create,
+    Interface* i) : ECLResource(p, create) {
+        interface_ = i;
+    }
+    
+    Interface* interface_;
+};
+
+
+/** Represents a single Link.
+ */
+class ECLLinkResource : public ECLResource {
+public:
+    ECLLinkResource(std::string p, clmessage::cl_message* create,
+                    const LinkRef& l, bool disc);
+    
+    /// Reference to the link that this Resource represents.
+    LinkRef link_;
+    
+    /// The state that the ECLModule knows the link to be in, independent of
+    /// the state in the link itself. This is useful for state changes that
+    /// originate at the CL rather than the BPA. 
+    Link::state_t known_state_;
+    
+    /// True if this link was discovered rather than created by the BPA.
+    bool is_discovered_;
+    
+    
+    /** Add a bundle to this link's outgoing bundle list.
+     *
+     * @param bundle  The bundle to be placed on the outgoing list. A new
+     *                BundleRef will be created for the bundle.
+     */
+    void add_outgoing_bundle(Bundle* bundle);
+    
+    
+    /** Retrieve a bundle from this link's outgoing bundle list.
+     *
+     * @param bundleid  The ID of the bundle to retrieve.
+     * 
+     * @return The OutgoingBundle object for the requested bundle if it
+     *         exists on the outgoing bundle list, NULL if it does not.
+     */
+    BundleRef get_outgoing_bundle(clmessage::bundle_attributes bundle_attribs);
+    
+    bool has_outgoing_bundle(Bundle* bundle);
+    
+    
+    /** Erase a bundle from this link's outgoing bundle list.
+     * 
+     * @param outgoing_bundle  The bundle to be removed from the outgoing list.
+     */
+    bool erase_outgoing_bundle(Bundle* bundle);
+    
+    
+    /** Get the actual outgoing bundle list.
+     * 
+     * This is used in ECLModule::cleanup() to iterate through the list.
+     */
+    BundleList& get_bundle_set();
+    
+    /** Set the link's high-water mark.
+     */
+    void set_high_water_mark(int high_water_mark) {
+        high_water_mark_ = high_water_mark;
+    }
+
+    /** Check if the high-water mark would be crossed.
+     * @param queued_bytes  The number of bytes queued on the link.
+     * @return  True if queued_bytes is greater than or equal to the link's
+     *          high-water mark; false otherwise.
+     */
+    bool high_water_mark_crossed(int queued_bytes) const {
+        return (high_water_mark_ > 0 && queued_bytes >= high_water_mark_);
+    }
+    
+    /** Set the link's low-water mark.
+     */
+    void set_low_water_mark(int low_water_mark) {
+        low_water_mark_ = low_water_mark;
+    }
+    
+    /** Check if the low-water mark would be crossed.
+     * @param queued_bytes  The number of bytes queued on the link.
+     * @return  True if queued_bytes is greater than or equal to the link's
+     *          low-water mark; false otherwise.
+    */
+    bool low_water_mark_crossed(int queued_bytes) const {
+        return (queued_bytes <= low_water_mark_);
+    }
+    
+private:
+    /// The list of bundles going out on this link.
+    BundleList outgoing_bundles_;
+    
+    /// The high-water mark for this link.
+    int high_water_mark_;
+    
+    /// The low-water mark for this link.
+    int low_water_mark_;
+};
+
+
+/** Hash function for a std::string.
+ */
+struct StringHash {
+    size_t operator()(std::string s) const {
+        size_t h = 0;
+        for (unsigned i = 0; i < s.length(); ++i)
+            h = h * 5 + s[i];
+        
+        return h;
+    }
+};
+
+typedef hash_multimap<std::string, ECLLinkResource*, StringHash>
+        LinkHashMap;
+
+
+/** The external convergence layer proxy on the DTN2 side.
+ * 
+ * This class interacts with DTN2 as any other conventional convergence layer.
+ * All interfaces and links on an external CLA appear to DTN2 to be on this
+ * CL.
+ * 
+ * Every Interface or Link intended for an external module must specify this
+ * convergence layer's name ('extcl') as its convergence layer at startup.  A
+ * parameter 'protocol={module name}' must appear before any CL-specific
+ * parameters for the resource to indicate which external module it is for.
+ * A list of any resources intended for an external module that has not yet
+ * connected to DTN2 is maintained (this is known as the 'unclaimed resource
+ * list').
+ */
+class ExternalConvergenceLayer : public ConvergenceLayer {
+public:
+    ExternalConvergenceLayer();
+    ~ExternalConvergenceLayer();
+
+    /** Start the ECLA listener thread.
+     * 
+     * This should be called before an instance of ExternalConvergenceLayer is
+     * given to ConvergenceLayer.
+     */
+    void start();
+    
+    bool set_cla_parameters(AttributeVector &params);
+    bool set_interface_defaults(int argc, const char* argv[],
+                                const char** invalidp);
+    bool interface_up(Interface* iface, int argc, const char* argv[]);
+    bool interface_down(Interface* iface);
+    void dump_interface(Interface* iface, oasys::StringBuffer* buf);
+    bool set_link_defaults(int argc, const char* argv[], const char** invalidp);
+    bool init_link(const LinkRef& link, int argc, const char* argv[]);
+    void delete_link(const LinkRef& link);
+    void dump_link(const LinkRef& link, oasys::StringBuffer* buf);
+    bool reconfigure_link(const LinkRef& link, int argc, const char* argv[]);
+    void reconfigure_link(const LinkRef& link, AttributeVector& params);
+    bool open_contact(const ContactRef& contact);
+    bool close_contact(const ContactRef& contact);
+    void bundle_queued(const LinkRef& link, const BundleRef& bundle);
+    void cancel_bundle(const LinkRef& link, const BundleRef& bundle);
+    bool is_queued(const LinkRef& link, Bundle* bundle);
+    void is_eid_reachable(const std::string& query_id, Interface* iface,
+                          const std::string& endpoint);
+    void query_link_attributes(const std::string& query_id,const LinkRef& link,
+                               const AttributeNameVector& attributes);
+    void query_iface_attributes(const std::string& query_id, Interface* iface,
+                                const AttributeNameVector& attributes);
+    void query_cla_parameters(const std::string& query_id,
+                              const AttributeNameVector& parameters);
+    void shutdown();
+
+    
+    /** Take unclaimed resources intended for a given protocol.
+     * 
+     * This will assign to the given module any resource on the unclaimed
+     * resource list matching the given protocol by setting the
+     * ECLResource::module field.  All matching resources are removed from the
+     * unclaimed resource list and returned.
+     * 
+     * @param protocol - The name of the protocol to match.
+     * @param owner - The module that will own the matching resources.
+     * 
+     * @return A list containing all resources that matched the protocol. 
+     */
+    std::list<ECLResource*> take_resources(std::string protocol);
+    
+    
+    /** Give a list of Interface resources back to the unclaimed resource list.
+     * 
+     * The resources will be placed back on the unclaimed resource list and
+     * the ECLResource::module field set to NULL.
+     */
+    void give_resources(std::list<ECLInterfaceResource*>& list);
+    
+    
+    /** Give a list of Interface resources back to the unclaimed resource list.
+     * 
+     * The resources will be placed back on the unclaimed resource list and
+     * the ECLResource::module field set to NULL.
+     */
+    void give_resources(LinkHashMap& list);
+    
+    
+    /** Delete a resource.
+     * 
+     * This will remove the resource from the list of unclaimed resources and
+     * call 'delete' on the pointer.
+     */
+    void delete_resource(ECLResource* resource);
+    
+    
+    /** Add a module to the active module list.
+     */
+    void add_module(ECLModule* module);
+    
+    
+    /** Remove a module from the active module list.
+     */
+    void remove_module(ECLModule* module);
+    
+    
+    /** Retrieve a module matching the given protocol name.
+     * 
+     * @param protocol - The name of the protocol to match.
+     * @return A pointer to the module matching the protocol, or NULL if no
+     *      such module exists.
+     */
+    ECLModule* get_module(const std::string& protocol);
+    
+    /// The path to the XSD file that specifies the XML messages between the
+    /// BPA and the CLA. This is set with the command 'ecla set xsd_file'
+    static std::string schema_;
+    
+    static bool client_validation_;
+    
+    /// The address on which the Listener thread will listen. This is set with
+    /// the command 'ecla set listen_addr'
+    static in_addr_t server_addr_;
+    
+    /// The address on which the Listener thread will listen. This is set with
+    /// the command 'ecla set listen_port'
+    static u_int16_t server_port_;
+    
+    static bool create_discovered_links_;
+    
+    static bool discovered_prev_hop_header_;
+    
+    /// This is used in ECLModule::send_message() to generate the XML
+    /// namespace/schema info at the start of every message.
+    static xml_schema::namespace_infomap namespace_map_;
+
+    
+    /// ECLModule locks this when it enters ECLModule::cleanup() to prevent
+    /// race conditions on resources that are being cleaned up.
+    oasys::Mutex global_resource_lock_;
+    
+    
+private:
+    /** Thread to listen for connections from new external modules.
+     * 
+     * This thread will wait on accept() for connections on the designated
+     * port (where this port is specified is TBD) on localhost for connections
+     * from new external modules.  For each new connection, an ECLModule is
+     * created and started.
+     */
+    class Listener : public oasys::TCPServerThread {
+    public:
+        Listener(ExternalConvergenceLayer& cl);
+        virtual ~Listener();
+
+        void start();
+        virtual void accepted(int fd, in_addr_t addr, u_int16_t port);
+
+    private:
+        /// Reference back to the convergence layer.
+        ExternalConvergenceLayer& cl_;
+    }; // class Listener
+    
+    
+    /** Add a resource to the unclaimed resource list.
+     */
+    void add_resource(ECLResource* resource);
+    
+    /** Convert an arg vector to a KeyValueSequence.
+     * 
+     * This will take a number of strings (in argv) in the form 'name=value'
+     * and convert them to a KeyValueSequence suitable for XML messages.
+     * 
+     * @param argc  Number of arguments in argv.
+     * @param argv  The vector of arguments.
+     * @param param_sequence  An instance of KeyValueSequence that will be
+     *                        populated with the parameters.
+     */
+    void build_param_sequence(int argc, const char* argv[],
+                              KeyValueSequence& param_sequence);
+    
+    /** Fill in the fields of a bundle_attributes object.
+     * 
+     * This will complete the bundle_attributes instance based on the given
+     * bundle.
+     */
+    void fill_bundle_attributes(const BundleRef& bundle,
+                                clmessage::bundle_attributes& attribs);
+        
+    /// The list of active modules.
+    std::list<ECLModule*> module_list_;
+    
+    /// Mutex for module_list_ access.
+    oasys::Mutex module_mutex_;
+    
+    /// The unclaimed resource list.
+    std::list<ECLResource*> resource_list_;
+    
+    /// Mutex for resource_list_ access.
+    oasys::Mutex resource_mutex_;
+    
+    /// The thread listening for new modules.
+    Listener listener_;
+};
+
+
+/** Functions for converting between various DTN2 types and their corresponding
+ * value in the clmessage namespace.
+ */
+class XMLConvert {
+public:
+    static clmessage::linkTypeType convert_link_type(Link::link_type_t type);
+    static Link::link_type_t convert_link_type(clmessage::linkTypeType type);
+    
+    static Link::state_t convert_link_state(clmessage::linkStateType state);
+    
+    static ContactEvent::reason_t convert_link_reason(
+            clmessage::linkReasonType reason);
+};
+
+} // namespace dtn
+
+#endif // XERCES_C_ENABLED && EXTERNAL_CL_ENABLED
+#endif // _EXTERNAL_CONVERGENCE_LAYER_H_
+