To create a custom connector, you need to:
derive a new class from rwsf::ConnectorImp
override the init(), start(), and stop() methods of this class
Here is some example code:
#include <iostream> #include <stdlib.h> #include <rwsf/core/Config.h> //1 #include <rwsf/message/AgentContext.h> //2 #include <rwsf/message/ConnectorImp.h> MyConnector : public rwsf::ConnectorImp { public: virtual void init(const rwsf::Config& config, const rwsf::AgentContext& context); virtual void start(); virtual void stop(); private: std::string dir_; };
//1 | The rwsf::Config class holds properties passed to the connector. For more information on the data and methods available through this class, see its class description in the Hydra Core Library Reference Guide. |
//2 | The rwsf::AgentContext class provides access to Agent resources. |
The following code shows the init() method for a connector that watches a directory. The name of the directory is passed to the connector in an rwsf::Config instance.
void MyConnector::init(const rwsf::Config& config, const rwsf::AgentContext& context) { ConnectorImp::init(config, context); dir_ = config.getInitParameter("directoryName"); ... // other needed initiation code }
The derived init() method should invoke the init() method of its parent so the parent class can also process any information from the rwsf::Config instance.
The start() method should be implemented as a non-blocking method, and should prepare the system that will wait for events and generate requests for the rest of the system. In most cases, the start() method spawns at least one thread that listens for requests. The Agent invokes the start() method after it has initialized all connectors and handlers.
void MyConnector::start() { workerThread_.start(); ... }
The stop() method should be implemented as a blocking call. The stop() method first releases any resources the connector owns and then stops the connector. The Agent invokes the stop() method when the Agent receives a signal to shut down.
void MyConnector::stop() { workerThread_.shutdown(); workerThread_.join(); ... }
A connector needs to do the following work:
Collect incoming requests off the wire and place data into an rwsf::MessageInfo instance. See the description of this class for information on the data structures and methods it provides. At a minimum, the connector should save any transport-level data in another rwsf::MessageInfo instance, and the message payload in an std::string instance. You should be aware of what data the handlers in the connector's handler chain require and be sure this data gets into the rwsf::MessageInfo instance.
Pass the rwsf::MessageInfo instance to a handler chain. Usually this will be the chain configured in rwagent.xml. However, the connector can retrieve and invoke an alternative chain, and can even not invoke any chain at all, although you won't be able to fully utilize the Agent if you don't.
The following table shows some of the main data structures in rwsf::MessageInfo.
Name | Type | Parent | Description |
rwsf:request | rwsf::MessageInfo | Data related to the request of this message. | |
rwsf:response | rwsf::MessageInfo | Data related to the response to this request. | |
rwsf:payload | std::string | rwsf:request and rwsf:response | The payload of a message. |
rwsf:transport | rwsf::MessageInfo | rwsf:request and rwsf:response | Data specific to the transport that was used. (e.g. HTTP headers, HTTP method, HTTP version, URI). |
Here is some example code:
void MyConnector::handleRequest() { std::string payload = something; // body of the request rwsf::RequestInfo request; request.set<std::string>("rwsf:payload", payload); rwsf::MessageInfo message; message.set<rwsf::RequestInfo>("rwsf:request", request); getHandlerChain().invoke(message); // uses the configured chain }
When the invoke has completed, the rwsf::MessageInfo should contain any information needed to send back a response. This information might include a response payload, headers, a status code, and so on. You need to implement code for writing that information to a location where the client can retrieve it.
Finally, use the following convenience macro that generates code to create a connector instance.
RWSF_DEFINE_CONNECTOR(MyConnector);
Compile your new connector into a shared library (DLL), and you are ready to load it into the Agent. You do this by creating a new rwsf:connector element in rwagent.xml with the appropriate configuration information. For example:
<rwsf:connector name="My_New_Connector" class="myconnector.createMyConnector" handlerChain="http"> <rwsf:property name="directoryName" value="/pub/upload"/> </rwsf:connector>
The name attribute is the string that represents the connector to the rest of the system. This string must be unique in the system.
The class attribute defines how to create an instance of this connector. In this example, myconnector is the name of the shared library that contains the connector code. The string createMyConnector is the name of the method that was created from the RWSF_DEFINE_CONNECTOR macro described above.
The handlerChain attribute specifies the name of the handler chain that this connector is tied to. The handler chain must be declared in rwagent.xml before the connector is declared. The example uses the handler chain that the HTTP/1.1 connector is using.
©2004-2007 Copyright Quovadx, Inc. All Rights Reserved.
Quovadx and Rogue Wave are registered trademarks of Quovadx, Inc. in the United States and other countries. All other trademarks are the property of their respective owners.
Contact Rogue Wave about documentation or support issues.