To create a custom connector, you need to:
derive a new class from rwsf::ConnectorImp
override the pure virtual methods init(), reinit(), start(), and stop().
Here is some example code:
#include <iostream> #include <stdlib.h> #include <rwsf/core/Config.h> //1 #include <rwsf/handlers/AgentContext.h> //2 #include <rwsf/handlers/ConnectorImp.h> MyConnector : public rwsf::ConnectorImp { public: virtual void init(const rwsf::Config& config, const rwsf::AgentContext& context); virtual void start(); virtual void stop(); virtual void reinit(const rwsf::Config& config, const rwsf::AgentContext& agent); //3 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 HydraExpress C++ API Reference Guide. |
//2 | The rwsf::AgentContext class provides access to Agent resources. |
//3 | reinit() must be in your implementation because it is a pure virtual function. However, this is just a hook for functionality in the HydraEnterprise product, so you should just implement this as an empty method. |
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.
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, which must include the servlet handler that knows how to dispatch the service request to the Agent servlet container.
Persist the message if persistence is turned on
The following table shows some of the main data structures in rwsf::MessageInfo.
Name | Type | Parent | Description |
rwsf:request | Data related to the request of this message. | ||
rwsf:response | 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: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); if( isPersist ) { // Persist request message rwsf::ConfigBinding requestBinding = rwsf::ConfigBinding::make("request"); requestBinding.setSimpleValue("message", dir_ ); rwsf::PersistentRequestStore* requestStore = context_.getPersistentRequestStore(); std::string persistenceKey = requestStore->persist(getPersistenceId(), context_.getAgentId(), persistenceFormat_, persistenceMedium_, requestBinding); // Set the persistence key in the rwsf::MessageInfo. This allows handlers // to remove the persisted request if desired message.set<std::string>(RWSF_KEY_REQUEST_UNIQUE_ID, persistenceKey); } 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 place it in the bin directory of your HydraExpress installation. You are now set to load the connector 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.
© Copyright Rogue Wave Software, Inc. All Rights Reserved. All Rights Reserved. Rogue Wave is a registered trademark of Rogue Wave Software, Inc. in the United States and other countries. HydraExpress is a trademark of Rogue Wave Software, Inc. All other trademarks are the property of their respective owners.
Contact Rogue Wave about documentation or support issues.