There are two senses in which a transport can be dynamic:
selecting transports at runtime
changing the transport for a service without having to change or recompile the implementation
The transports and listeners defined in the transport configuration files are all identified by a name. 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 WebSphere MQ requesting quotes for the items in a purchase order.
The server takes messages off the queue, parses the purchase order and develops a quote based on the items in the purchase order. The server then sends a notification message with the quote back to the requestor, using WebSphere MQ or email, depending on what a particular client prefers.
This service is essentially synchronous in the sense that the server processes a request and sends back a response as soon as possible. However, perhaps this server has to process a lot of purchase orders, so it cannot always respond immediately. Using a message queue for the client request allows the client to avoid a wait if the quotation is some time in coming back. Also, decoupling the request and response by using one-way messaging and notification allows the server to use a different transport to send the response if that is necessary or desirable.
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) { PurchaseOrder po = getPurchaseOrder("A2Z Incorporated"); //1 rwsf::CallInfo callInfo; callInfo.addRequestSoapHeader(rwsf::XmlName("replyBy", rwsf::XmlNamespace("headers", "http://www.roguewave.com/examples/webservices/headers")), "email") //2 callInfo.addRequestSoapHeader(rwsf::XmlName("address", rwsf::XmlNamespace("headers", "http://www.roguewave.com/examples/webservices/headers")), "purchasing@polyglot.com") //2 proxy.sendForQuote(callInfo, po); //3 } int main(int argc, char* argv[]) { // code that handles arguments, determines the // URL location, default transport type, and so on ... ... rwsf::Transport transport; transport = rwsf::TransportManager::findTransport("IBMMQ"); //4 transport.setProperty("location",location); //5 ... GetQuotesProxy proxy = GetQuotesProxy::make(transport); //6 invoke_sendPOForQuote(proxy) //7 }
//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 | Adds SOAP header elements specifying that notification should be sent by email and the email address to send it to. |
//3 | Invokes the client proxy sendForQuote() one-way operation method. |
//4 | Resets the default transport to the WebSphere MQ transport. |
//5 | Sets the location property in the new transport. |
//6 | Creates a proxy correctly set up to use WebSphere MQ to send a purchase order to the location for the A2Z incorporated server. |
//7 | Invokes the service operation method for sending a purchase order for quote. |
On the server side, the implementation decides based on the SOAP header value how to send the notification to the client.
void GetQutoesImp::sendForQuote(rwsf::CallInfo& callInfo, const PurchaseOrder& request_in) { Quote quote = provideQuote(request_in); //1 if(callInfo.getRequestSoapHeaderValue(rwsf::XmlName("replyBy", rwsf::XmlNamespace("headers", "http://www.roguewave.com/examples/webservices/headers"))) =="email") //2 { std::string emailAddress = callInfo.getRequestSoapHeaderValue(rwsf::XmlName("address", rwsf::XmlNamespace("headers", "http://www.roguewave.com/examples/webservices/headers"))); //3 rwsf::transport transport = rwsf::TransportManager::findTransport("SMTP"); //4 transport.setProperty("email-address", emailAddress); //5 GetQuotesNotificationProxy proxy = GetQuotesNotificationProxy::make(transport); //6 proxy.reply(callInfo, quote); //7 } else { // Create ClientManager that knows to use the MQ transport, // obtain an MQ-enabled proxy, and return the quote. } }
//1 | Sends the incoming purchase order to a method that returns a quote. |
//2 | Tests how the reply is to be sent. |
//3 | For email, knows to obtain the SOAP header value with the email address. |
//4 | Creates a transport object that sends messages by SMTP. |
//5 | Sets the email transport email-address property to the email address of the company requesting the quote. |
//6 | Obtains an SMTP-enabled proxy. |
//7 | Returns the quote via email. |
Defining named transports in the transport configuration files allows you to make a service independent of the transport it uses. 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 server-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.
©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.