Web Service Development Guide : PART IV Extending your Applications : Chapter 11 Custom Transports and Listeners : Making Transports and Listeners Dynamic
Making Transports and Listeners Dynamic
There are two senses in which a transport or listener can be dynamic:
selecting a transport or listener at runtime, “Selectable Transports and Listeners”
changing the transport or listener for a service without having to change or recompile the implementation, “Interchangeable Transports and Listeners”
NOTE >> This section uses transport code for illustration, but the dynamic concept applies equally to listeners.
Selectable Transports and Listeners
The transports and listeners defined in the transport configuration files are all identified by a name. At runtime, either the client or the server (when sending notifications or solicit messages) can select by name the transport to be used to send a message.
Let’s consider the following scenario:
The clients for a service send one-way messages via HTTP requesting quotes for the items in a purchase order.
The service parses the purchase order and develops a quote based on the items in the purchase order. The service then sends a notification message with the quote back to the requestor using HTTP or email, depending on the client’s preference.
Let’s suppose that the name of this service is GetQuotes and that it is provided by a company called A2Z Incorporated. The code below shows the code in the client implementation that sends off a purchase order for quotation.
 
void invoke_sendForQuote(GetQuotesProxy& proxy, std::string location)
{
PurchaseOrder po = getPurchaseOrder("A2Z Incorporated"); //1
rwsf::CallInfo callInfo;
proxy.sendForQuote(callInfo, po, location); //2
}
 
int main(int argc, char* argv[]) {
if( argc < 3 ) {
printf("Usage: %s [request url] [email address | url]\n", argv[0]); //3
return 0;
}
std::string serivceURL(argv[1]);
std::string location(argv[2]);
rwsf::Transport transport;
rwsf::Transport transport = //4
rwsf::TransportManager::findTransportByUrl(serviceURL);
...
GetQuotesProxy proxy = GetQuotesProxy::make(transport); //5
 
invoke_sendForQuote(proxy, location); //6
}
//1 Calls some method that creates a PurchaseOrder object from the items that need to be ordered from the company named in the parameter.
//2 Invokes the client proxy sendForQuote() one-way operation method, passing in the location to send the quote to, either an email address or a URL.
//3 If the correct number of arguments (two) is not provided, prints “Usage...”
//4 Gets the transport to use for sending the request, based on the URL.
//5 Creates a proxy with the specified transport, used for sending a purchase order to the location for the A2Z incorporated server.
//6 Invokes the service operation method for sending a purchase order for quote.
On the server side, the implementation evaluates how to send the notification, based on the location:
 
void GetQuotesImp::sendForQuote(rwsf::CallInfo& callinfo,
const PurchaseOrder& po,
std::string location)
{
Quote quote = provideQuote(po); //1
 
rwsf::Transport transport;
If( isEmailAddress(location) ) { //2
transport = rwsf::TransportManager::findTransport(“SMTP”);
transport.setProperty(“email-address”, email); //3
}
else
transport = rwsf::TransportManager::findTransportByUrl(location); //4
GetQuotesNotificationProxy proxy =
GetQuotesNotificationProxy::make(transport); //5
 
proxy.reply(callInfo, quote); //6
 
}
//1 Sends the incoming purchase order to a method that returns a quote.
//2 If the location provided is an email address, creates a transport object that sends messages by SMTP, which must be a named transport defined in the transports.xml configuration file.
//3 Sets the email transport email-address property to the email address of the company requesting the quote.
//4 Otherwise, creates a transport based on the provided URL location.
//5 Obtains a proxy based on the selected transport.
//6 Returns the quote via email.
This example has shown how to select between two possible transports at runtime, from both the client and server. Next let’s consider how to change the type of a named transport by altering the configuration file and thereby avoiding any changes to the code that would require recompiling.
Interchangeable Transports and Listeners
Defining named transports or listeners in the transport and listener configuration files allows you to make a service independent of the transport it uses, or a client independent of the listener it uses if it is receiving messages using the notification or solicit-response patterns.
Let’s say, for example, that you maintain a service for providing stock quotes over HTTP. Instead of using the default HTTP transport element in transports.xml and client-transports.xml, you could do this:
 
<rwsf:transports
xmlns:rwsf="http://www.roguewave.com/rwsf/transport">
<rwsf:transport name="HTTP"
uri="http://schemas.xmlsoap.org/soap/http" scheme="http"
default="true" class="rwsf_webservice.createHttpTransport">
<rwsf:property name="connect-timeout" value="60000"/>
<rwsf:property name="submit-timeout" value="60000"/>
<rwsf:property name="reply-timeout" value="60000"/>
</rwsf:transport>
<rwsf:transport name="StockQuoteService"
uri="http://schemas.xmlsoap.org/soap/http" scheme="http"
default="false" class="rwsf_webservice.createHttpTransport">
<rwsf:property name="connect-timeout" value="60000"/>
<rwsf:property name="submit-timeout" value="60000"/>
<rwsf:property name="reply-timeout" value="60000"/>
</rwsf:transport>
You now have two entries with scheme attributes that identify them as HTTP and with class attributes that point to the HydraExpress library HTTP transport object. The HydraExpress default HTTP entry remains the default, but the service implementation code would explicitly use the StockQuoteService transport:
 
rwsf::transport transport =
rwsf::TransportManager::findTransport("StockQuoteService");
StockQuoteServiceProxy proxy =
StockQuoteServiceProxy::make(transport);
If you later decide that HTTP is not providing sufficient performance, you could create a binary transport, point the StockQuoteService transport entries at that transport, and continue on with no need to change or recompile the service code. The entries in the transport configuration files might then look something like this:
 
<rwsf:transport name="StockQuoteService"
uri="http://www.a2zincorported.com/transports/binary/"
scheme="binary" default="true"
class="binarytransport.createBinaryTransport">
<rwsf:property name="some-binary-property" value="whatever"/>
...
</rwsf:transport>
There are a couple more details to completing such a switchover:
You must restart the server to pick up the new binary transport.
You must distribute to the clients the modified client-transports.xml file and the new binary transport object.
But the service implementation code would simply use the binary transport now associated with the service.
The next section outlines the process of creating and configuring a custom transport.