Making Transports and Listeners Dynamic
There are two senses in which a transport or listener can be dynamic:
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
}
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
}
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.