--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/servlib/conv_layers/ECLModule.h Thu Apr 21 14:57:45 2011 +0100
@@ -0,0 +1,375 @@
+/* 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 _ECLMODULE_H_
+#define _ECLMODULE_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 <semaphore.h>
+#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/thread/MsgQueue.h>
+#include <oasys/io/TCPClient.h>
+#include <oasys/serialize/XercesXMLSerialize.h>
+
+#include "ExternalConvergenceLayer.h"
+#include "CLEventHandler.h"
+#include "clevent.h"
+
+#define POST_MESSAGE(module_ptr, message_name, message) do { \
+ cl_message* container = new cl_message(); \
+ container->message_name(message); \
+ module_ptr->post_message(container); \
+} while (false);
+
+namespace dtn {
+
+using __gnu_cxx::hash_multimap;
+using __gnu_cxx::hash;
+
+
+/** Handles communication to and from an external module.
+ *
+ * There is one ECLModule thread for each external module that connects to DTN2.
+ * Messages can be sent to the module with post_event(CLEvent*) method.
+ */
+class ECLModule : public CLInfo,
+ public CLEventHandler,
+ public oasys::Thread {
+
+//ExternalConvergenceLayer must be able to check for a link
+friend class ExternalConvergenceLayer;
+
+public:
+ /** Construct a new ECLModule after the module initiates contact.
+ *
+ * @param fd - The socket descriptor that the module connected on.
+ * @param remote_addr - The address of the connecting module (probably
+ * localhost).
+ * @param remote_port - The port number of the connecting module.
+ * @param cl - A reference back to the convergence layer.
+ */
+ ECLModule(int fd, in_addr_t remote_addr, u_int16_t remote_port,
+ ExternalConvergenceLayer& cl);
+ virtual ~ECLModule();
+
+ virtual void run();
+
+
+ /** Post an event for transmission to the external module.
+ *
+ * @param event - The message to be transmitted. This class assumes
+ * responsibility for deleting the event when it is no longer needed.
+ */
+ void post_message(cl_message* message);
+
+
+ /** Have the module take a resource.
+ *
+ * If the resource is an interface, it will be added to iface_list_. If it
+ * is a link, it will be added to normal_links_. In either case, the
+ * resource's 'create_message' will be sent to the ECLA.
+ */
+ void take_resource(ECLResource* resource);
+
+ /** Shut down this module's thread.
+ *
+ * The thread will be interrupted by notifying message_queue_. This will
+ * set was_shutdown_ true and call set_should_stop(). There are two
+ * important differences between a normal shutdown from a CLA that
+ * disconnects and a forced shutdown through this method:
+ *
+ * <ul><li>
+ * The DELETE_ON_EXIT is not set for the thread after calling this
+ * method, so the caller is responsible for deleting this object.
+ * <li>
+ * ExternalConvergenceLayer::remove_module() is not called after
+ * calling this method.
+ * </ul>
+ */
+ void shutdown();
+
+ /** Remove an interface from the list of active interfaces.
+ *
+ * This should be called after sending a CLInterfaceDestroyRequest to the
+ * module in order to clean up any resources used locally for the interface.
+ * The interface will not be given back to the unclaimed resource list, so
+ * it will not reappear if the module restarts unless it is explicitly
+ * created again.
+ *
+ * @param name - The name of the interface to remove.
+ *
+ * @return A pointer to the ECLInterfaceResource that was just removed, or
+ * NULL if the specified interface does not exist. This pointer should
+ * be deleted by the caller.
+ */
+ ECLInterfaceResource* remove_interface(const std::string& name);
+
+
+ /** Retrieve the module's name, which should be the name of the protocol
+ * that it supports.
+ */
+ const std::string& name() const { return name_; }
+
+protected:
+ void handle(const cla_add_request& message);
+ void handle(const cla_delete_request& message);
+ void handle(const cla_params_set_event& message);
+ void handle(const interface_created_event& message);
+ void handle(const interface_reconfigured_event& message);
+ void handle(const eid_reachable_event& message);
+ void handle(const link_created_event& message);
+ void handle(const link_opened_event& message);
+ void handle(const link_closed_event& message);
+ void handle(const link_state_changed_event& message);
+ void handle(const link_deleted_event& message);
+ void handle(const link_attribute_changed_event& message);
+ void handle(const link_add_reachable_event& message);
+ void handle(const contact_attribute_changed_event& message);
+ void handle(const bundle_transmitted_event& message);
+ void handle(const bundle_canceled_event& message);
+ void handle(const bundle_receive_started_event& message);
+ void handle(const bundle_received_event& message);
+ void handle(const report_eid_reachable& message);
+ void handle(const report_link_attributes& message);
+ void handle(const report_interface_attributes& message);
+ void handle(const report_cla_parameters& message);
+
+ /** Take resources from the resource list that belong to this module.
+ */
+ void take_resources();
+
+ /** Check if a link exists.
+ */
+ bool link_exists(const std::string& name) const;
+
+private:
+ /// The size of the buffer used by read_cycle().
+ static const size_t READ_BUFFER_SIZE = 256;
+
+ /// The maximum amount of the bundle that we will map when sending to and
+ /// receiving from the module.
+ static const size_t MAX_BUNDLE_IN_MEMORY = (256 * 1024);
+
+ /** Read a bundle file and create a Bundle* from it.
+ *
+ * This does the actual work of reading the bundle file and feeding it to
+ * BundleProtocol::consume(). If any error occurs, a message is logged and
+ * the method returns early. Otherwise, if the file is read successfully,
+ * a BundleReceivedEvent will be posted to the BundleDaemon.
+ *
+ * This is used by handle(bundle_received_event) to handle a fully received
+ * bundle and by cleanup() to handle partial bundles for which we received
+ * a bundle_receive_started_event but no bundle_received_event when
+ * a module goes away.
+ *
+ * @param location The file name of the bundle, relative to bundle_in_path_.
+ */
+ void read_bundle_file(const std::string& location, const std::string& peer_eid);
+
+ /** Make the next pass when there is input on the socket.
+ *
+ * As input comes in on the socket, this will make one pass of at most
+ * READ_BUFFER_SIZE bytes. Based on the current state of things, this could
+ * read the XML document, parse the document into a CLEvent, and dispatch
+ * the completed CLEvent. The general idea is that this function should
+ * just keep getting called from the main loop whenever input is available
+ * and it will do the above actions as it needs to and is able to.
+ */
+ void read_cycle();
+
+ /** Send a message to the external module.
+ *
+ * This will build the XML document from the CLEvent and send it
+ * out on the socket.
+ *
+ * @param event - The event to send. Note that this pointer is not deleted
+ * by this method.
+ *
+ * @return 0 if the message was successfully sent, -1 if it was not.
+ */
+ int send_message(const cl_message* message);
+
+ /** Prepare a bundle to be sent by the CLA.
+ *
+ * This must be called before a CLBundleSendRequest can be sent by
+ * send_event(). The entire bundle (all headers and payload) will be
+ * written to a file in bundle_out_path_ exactly as it should be sent by
+ * the CLA. The 'location' attribute of 'event' will be filled with the
+ * name of this file (relative to bundle_out_path_).
+ *
+ */
+ int prepare_bundle_to_send(cl_message* message);
+
+
+ /** Clean up after a bundle when it cannot be sent.
+ *
+ * This is called by prepare_bundle_to_send() when something goes wrong
+ * and by cleanup() when a module dies while it still has bundles waiting
+ * to be sent. It will remove the bundle from the link's outgoing list (if
+ * erase_from_list is true), and erase the bundle file from storage.
+ *
+ * @note The 'path' field of outgoing_bundle must be set before calling
+ * this method.
+ *
+ * @param link_resource The holder for the link on which this bundle was
+ * to be sent.
+ * @param outgoing_bundle The holder for the bundle that was not sent.
+ * @param erase_from_list If true, erase this bundle from link_resource's
+ * outgoing bundle list.
+ *
+ */
+ void bundle_send_failed(ECLLinkResource* link_resource,
+ Bundle* bundle,
+ bool erase_from_list);
+
+ /** Retrieve an interface from the interface list.
+ *
+ * @param name - The name of the interface to find.
+ * @return The ECLInterfaceResource for the requested interface, or NULL
+ * if it does not exist.
+ */
+ ECLInterfaceResource* get_interface(const std::string& name) const;
+
+
+ /** Retrieve a link from the link lists.
+ *
+ * @param name - The name of the link to find.
+ * @return The ECLLinkResource for the requested link, or NULL
+ * if it does not exist on either normal_links_ or temp_links_.
+ */
+ ECLLinkResource* get_link(const std::string& name) const;
+
+
+ /** Create a link in response to another node's link creation to us.
+ *
+ * This is called when a CLLinkCreatedEvent has the 'discovered' field
+ * set true, which means that another node has opened a link to us
+ * and we need one to go back the other way.
+ *
+ * The created ECLLinkResource is placed on temp_link_list_, rather
+ * than link_list_, so that we do not try to give it back to the CL
+ * when the external module closes.
+ *
+ * @param peer_eid - The EID of the node that opened the connection to us.
+ * @param nexthop - The next hop to the other node.
+ * @param link_name - The name of the link, specified by the external
+ * module.
+ * @return The ECLLinkResource created for this link. This resource
+ * has been placed in temp_link_list_.
+ */
+ ECLLinkResource* create_discovered_link(const std::string& peer_eid,
+ const std::string& nexthop,
+ const std::string& link_name);
+
+ /** Clean up state for this module after loosing contact with it.
+ *
+ * This will do the work of closing down links, returning resources, and
+ * canceling bundles. All links will be made CLOSED with a
+ * LinkStateChangeRequest. Any ECLResources that we own (in iface_list_ and
+ * normal_links_) will be returned to ExternalConvergenceLayer's unclaimed
+ * resource list. Links in temp_links_ will be destroyed. Finally, the
+ * incoming and outgoing bundle directories will be removed.
+ */
+ void cleanup();
+
+ static void update_contact_attributes(const contact_attributes& attributes,
+ const ContactRef& contact);
+
+ /// This module's protocol name.
+ std::string name_;
+
+ /// The path where bundles going out to the CLA are stored.
+ std::string bundle_in_path_;
+
+ /// The path where bundles comming in from the CLA are stored.
+ std::string bundle_out_path_;
+
+ /// Indicates that this ECLModule is shutting down due to a call to
+ /// shutdown().
+ bool was_shutdown_;
+
+ /// Raw data buffer for read_cycle().
+ char read_buffer_[READ_BUFFER_SIZE];
+
+ /// String buffer for read_cycle().
+ std::vector<char> msg_buffer_;
+
+ /// Reference back to the convergence layer.
+ ExternalConvergenceLayer& cl_;
+
+ /// Structure used by incoming_bundle_list_
+ struct IncomingBundleRecord {
+ std::string location;
+ std::string peer_eid;
+ };
+
+ /// The list of bundles for which we have received a
+ /// bundle_receive_started_event but no bundle_received_event.
+ std::list<IncomingBundleRecord> incoming_bundle_list_;
+
+ /// The interface list.
+ std::list<ECLInterfaceResource*> iface_list_;
+
+ /// Mutex protecting iface_list_.
+ oasys::Mutex iface_list_lock_;
+
+ /// The list of normal (non-temporary) links.
+ LinkHashMap link_list_;
+
+ /// Semaphore for normal_links_ and temp_links_. It is OK for two threads
+ /// to read either of these at the same time, but they must be locked
+ /// when makeing changes to them.
+ mutable sem_t link_list_sem_;
+
+ /// The socket to the external module.
+ oasys::TCPClient socket_;
+
+ /// The queue for messages to be sent to the external module.
+ oasys::MsgQueue<cl_message*> message_queue_;
+
+ /// The parser used to parse XML messages from the external module.
+ oasys::XercesXMLUnmarshal parser_;
+};
+
+} // namespace dtn
+
+#endif // XERCES_C_ENABLED && EXTERNAL_CL_ENABLED
+#endif // _ECLMODULE_H_