Web Service Development Guide : PART IV Extending your Applications : Chapter 14 SOAP Message Handlers : Creating Your Own Handlers
Creating Your Own Handlers
This section includes a simple example for creating your own handlers. It uses an example in the <installdir>\examples\webservices\Handlers directory. This directory contains provided client and server implementations, two handler classes, and configuration files required to configure the handlers.
On the client, we will add two handlers to the proxy: a request handler and a transport handler.
On the server, the same handler will take the SOAP header and perform special processing before adding a response header back in for the response message.
Creating a customized handler is a simple task, requiring the following basic steps:
Generate code from a WSDL, as usual. In this example, we’ll use handlers.wsdl.
Write the handler implementation, extending rwsf::MessageHandlerImp.
Add the handler to the proxy.
Register the handler by defining it in the service implementation and adding it to the servicecontextname_objects.xml file.
Chain and configure the handler in servicecontextname_handlers.xml.
Generate Code
Before we create a custom handler, we’ll generate code from the supplied handlers.wsdl. Open a command prompt and change to the directory <installdir>\examples\webservices\Handlers. Then generate code as following:
 
prompt>rwsfgen example-project.xml
NOTE >> The HydraExpress project file example-project.xml includes all options and files to be used as an argument to the code generator, so using this file results in exactly the same generated code as providing the options and files directly. For more information on the use of HydraExpress project files in the shipped examples, see “The Use of the HydraExpress Project File in Shipped Examples,” in the HydraExpress User Guide.
HydraExpress will generate code based on handlers.wsdl into a directory, HandlersExample. For basic information on how to generate code, see Chapter 3, “Creating a Web Service.”
Now we’re ready to create a handler.
Write the Handler Implementation
The two handlers included in this example are SoapSecurityHandler, a request handler, and StringReverseHandler, a transport handler. Both derive from base class rwsf::MessageHandlerImp.
rwsf::MessageHandlerImp is the body class of a handle/body pair. You derive from this class and implement the method invoke(). This method takes an rwsf::CallInfo object containing any data or information to be included with the message.
Creating a Request Handler
First, we'll create a request handler to handle requests for both the client and the server. A request handler processes the message body itself.
This example uses rwsf::CallInfo’s isClient() method (“Response Handlers”) to determine whether the request is client or server-based, and then either adds the SOAP header, or gets its value. Here is the included class SoapSecurityHandler.
 
#include <rwsf/webservice/MessageHandler.h>
 
class SoapSecurityHandler: public rwsf::MessageHandlerImp { //1
 
public:
 
virtual void invoke(rwsf::CallInfo& callInfo)
{
// is this the client?
if(callInfo.isClient()) { //2
callInfo.addRequestSoapHeader(
rwsf::XmlName("Security"), "SomeSecurityData");
}
else { //3
std::string value = callInfo.getRequestSoapHeaderValue(
rwsf::XmlName("Security"));
// add the value back to the response headers
callInfo.addResponseSoapHeader(
rwsf::XmlName("SecurityResponse"), value + ": From Server");
}
}
};
//1 Derives from rwsf::MessageHandlerImp.
//2 If isClient is true, this is the client and not the server. If it’s the client, let’s add a request SOAP header containing security data.
//3 Or, if this is the server, get the client’s SOAP header and add it to the response header.
Creating a Transport Handler
A transport handler processes the message after it is created and before it is transported. Our transport handler will process both the request and the response. Let’s look at the transport handler class, StringReverseHandler.
 
#include <rwsf/webservice/MessageHandler.h>
 
class StringReverseHandler : public rwsf::MessageHandlerImp { //1
 
public:
virtual void invoke(rwsf::CallInfo& callInfo)
{
std::string data;
 
if(callInfo.isRequest()) //2
data = callInfo.getRequest();
else
data = callInfo.getResponse();
 
std::string newData;
newData.reserve(size_t(data.length()));
 
for(int i = data.length()-1; i >= 0; --i) { //3
newData.append(data[i]);
}
 
if(callInfo.isRequest()) //4
callInfo.setRequest(newData);
else
callInfo.setResponse(newData);
}
};
//1 Derives from rwsf::MessageHandlerImp.
//2 If isRequest is true, this is a request from a client, so get it. Otherwise, this is a response from the server, so get that.
//3 Reverse the bytes in the message. This just demonstrates how to change the message before it is sent over the transport.
//4 If this is a request, add the changed message to the request; otherwise, add it to the response.
Now that you’ve created your handlers, they are ready to add to the client proxy.
Aborting Handler Processing
If you wish to stop the further processing of a message by the message processing layer (for instance, based on a SOAP header it contains) you may do so.
From your handler implementation, simply invoke
 
callInfo.stopMessageProcessing();
This method aborts the further processing of a message.
The handler has responsibility for any other necessary handling, such as setting a response if a response is expected.
The service transport handlers that were invoked on the request message are invoked on the response message as well before it is returned to the client. For example, if a request was processed by a transport handler that decompressed the message, the response message provided by the aborted handler would be compressed on the way out.
Add the Handlers to the Proxy
The client proxy is itself a handler, and also supports chaining handlers. For general information on how to use the client proxy, see Chapter 7, “Developing Clients.”
To add new handlers to the proxy, just invoke the appropriate “add handler” methods on the proxy after you call the make() method. Following is an excerpt from the supplied implementation of the proxy, HandlersClient.cpp.
 
HandlersProxy proxy = HandlersProxy::make(location);
 
proxy.addTransportHandler(new StringReverseHandler);
proxy.addServiceRequestHandler(new SoapSecurityHandler);
 
invoke_handler(proxy);
The order in which handlers are processed on the client affects which type of handlers you choose to add:
Transport handlers are applied symmetrically, with the first handler added being closest to the transport in both directions. A second transport handler would be one position removed from the transport in both directions, and so on.
Request and response handlers are applied in the order added.
This is illustrated in Figure 8.
Register the Handlers
There are two ways to register your new handlers. The easiest way is to define them in the server implementation, using the macro RWSF_DEFINE_MESSAGE_HANDLER. This method is the easiest because it requires no other changes. Following is an excerpt from the supplied server implementation, HandlersImp.cpp.
 
#include "StringReverseHandler.h" //1
#include "SoapSecurityHandler.h"
 
RWSF_DEFINE_MESSAGE_HANDLER(StringReverseHandler) //2
RWSF_DEFINE_MESSAGE_HANDLER(SoapSecurityHandler)
//1 Include the handler header files.
//2 Use the RWSF_DEFINE_MESSAGE_HANDLER macro to register your handlers.
The disadvantage of the above method is that it ties the handlers to the service implementation when really they should be independent of it. One way to achieve a decoupling of the handlers from the service implementation is by declaring the RWSF_DEFINE_MESSAGE_HANDLER macros in the implementation files for the handlers themselves and compiling the handlers into a separate library.
To complete the registration of your handlers, simply add them to the handlers_objects.xml file. This file is loaded by the Agent at startup, making all objects in it available to any running service. Let’s take a look at the relevant lines in the supplied handlers_objects.xml:
 
<naming-obj> <!-- 1 -->
<naming-name>SoapSecurityHandler</naming-name>
<naming-class>HandlersService.createSoapSecurityHandler <!-- 2 -->
</naming-class>
</naming-obj>
<naming-obj>
<naming-name>StringReverseHandler</naming-name>
<naming-class>HandlersService.createStringReverseHandler
</naming-class>
</naming-obj>
//1 Add a naming-obj element for each handler. The element must include a naming-name and a naming-class attribute.
//2 The naming-class attribute is made up of library.createClass where “library” is the name of the service and the shared library (or dll on Windows), and “Class” is the name of the class to instantiate.
Configure the Handlers
To configure and chain your handlers, add them to handlers_handlers.xml in the appropriate chain.
The order in which handlers are applied is based on their order in the configuration file. Transport handlers are applied symmetrically at input and output, with the first configured handler being closest to the transport, the second configured handler being second from the transport, and so on. Request and response handlers are applied in configured order in both directions.
For more information on handler processing order, see “Types of SOAP Handlers”.
 
<configuration>
<service name="HandlersService">
<request-handlers> <!-- 1 -->
<handler name="SoapSecurityHandler"/>
<handler
name="http://localhost:8090/handlers/HandlersSkeleton"/>
</request-handlers>
<transport-handlers>
<handler name="StringReverseHandler"/> <!-- 2 -->
</transport-handlers>
<service-endpoint
name="http://localhost:8090/handlers/Handlers"/>
<response-handlers>
</response-handlers>
<fault-handlers>
</fault-handlers>
</service>
</configuration>
//1 Add the request handler by its name attribute and the name of its skeleton.
//2 Add the transport handler.
Compile and Deploy the Service
Before building this example, copy all provided sample files from <installdir>\examples\webservices\Handlers to the new HandlersExample directory, allowing the provided files to overwrite the generated files, as follows:
HandlersClient.cpp
Copy to HandlersExample\app\client
HandlersImp.cpp
Copy to HandlersExample\app\server
SoapSecurityHandler.h
StringReverseHandler.h
Copy to HandlersExample\app\client
handlers_handlers.xml
handlers_objects.xml
Copy to HandlersExample\conf
To compile the client and server, change to the directory HandlersExample, and run nmake (Windows) or make (Linux or UNIX).
To deploy the Agent, run nmake (Windows) or make (Linux or UNIX) for the deploy target, and then start the server.
NOTE >> If the Agent is already running, you must first stop it before you deploy your service, using the rwsfserver stop command.
Windows
rwsfserver stop
nmake deploy
rwsfserver start
Unix/Linux
rwsfserver stop
make deploy
rwsfserver start
For more information on compiling and deploying, see Chapter 22.
Change directories to HandlersExample\bin, and run the client by entering:
Windows
prompt>HandlersClient.exe
Unix/Linux
prompt>HandlersClient
The server should return:
return value = 110
Security value = SomeSecurityData: From Server