One the client-side, we need to send a request for weather updates, using the generated sample client WeatherSummaryClient.cpp. It does the following:
Sends a request for a weather summary to the server, which sends back the WeatherSummary object created in the server implementation above.
Creates a WeatherSummary object with different data and sends it to the server as a one-way weather update message.
Listens for a notification message from the server, which should return the same data sent as the weather update.
For each step, the code writes to standard out the WeatherSummary object being sent or received as verification that the service is running as expected.
First we have the implementation for invoke_getSummary(), which sends a request for a weather summary to the server and marshals out the returned WeatherSummary object:
void invoke_getSummary(WeatherSummaryProxy& proxy) { rwsf::CallInfo info; try { wsx::WeatherSummary ws = proxy.getSummary(info, "97330"); //1 std::cout << "Marshaling WeatherSummary received from server:" << std::endl << std::endl; std::cout << ws.marshal() << std::endl << std::endl; //2 } catch(const rwsf::SoapFaultException& e) { //3 std::cout << "Fault Code: " << e.getFault().getFaultcode().asString() << std::endl; std::cout << "Fault String: " << e.getFault().getFaultstring() << std::endl; } }
//1 | Invokes the getSummary() method in the proxy and places the WeatherSummary object returned into ws. |
//2 | Marshals the WeatherSummary object as an XML document. |
//3 | Catches any faults returned from the server and prints them. |
Next we have the implementation for invoke_updateWeather(), which creates a WeatherSummary object and sends it to the server as a one-way weather update message.
void invoke_updateWeather(WeatherSummaryProxy& proxy) { wsx::WeatherSummary ws; //1 ws.setSky("overcast"); //2 ws.setTemp(54); ws.setWindSpeed(12); ws.setZipcode(97584); std::string host_in (rwsf::createStlString(listener.getProperty("bound-ip"))); //3 std::string port_in (rwsf::createStlString(listener.getProperty("bound-port"))); std::string transportName_in (rwsf::createStlString(listener.getProperty("name"))); std::cout << "Marshaling weather summary sent to server" << std::endl << "as one-way message:" << std::endl << std::endl; std::cout << ws.marshal() << std::endl << std::endl; //4 rwsf::CallInfo callInfo; proxy.updateWeather(callInfo, host_in, port_in, transportName_in, ws); //5 }
//1 | Creates a WeatherSummary object. |
//2 | Populates WeatherSummary object with some data. |
//3 | This and the following lines create parameters to be used in the method call. These parameters are derived from the listener created in the main() method (described below). They communicate to the server the transport type and location being used to send the message. |
//4 | Marshals the WeatherSummary object as an XML document. |
//5 | Sends a one-way message to the server with the weather summary update. |
Finally, here is the code that appears in the main() function of the client implementation:
int main(int argc, char* argv[]) { // ... code that handles arguments and determines the // URL location and transport type try { // code that starts up the listener, which listens // for notifications from the server ... listener.start(); //1 ... rwsf::Transport transport; //2 // code that obtains the transport for communicating // with the server ... WeatherSummaryProxy proxy = WeatherSummaryProxy::make(transport); //3 if(!proxy.isValid()) { //4 std::cerr << "Unable to create proxy. " << std::endl; std::cerr << "Exiting." << std::endl; return 0; } invoke_getSummary(proxy); //5 invoke_updateWeather(proxy,listener); //6 rwsf::MessageHandler serviceImp = rwsf::HandlerManager::findHandler ("WeatherSummaryNotificationImp"); //7 WeatherSummaryNotificationImp& notifServiceImp = (WeatherSummaryNotificationImp&)serviceImp.body(); //8 while (!notifServiceImp.finalMessageReceived()); //9 std::cout << std::endl << "Shutting down Listener." << std::endl; listener.stop(); //10 } // usual catch blocks { ... } return 0; }
//1 | Because notification is involved, the generated code starts a listener for the appropriate transport to listen for incoming notifications. |
//2 | Creates a rwsf::Transport instance. The code that follows (not shown) determines the correct transport and assigns it to this instance. |
//3 | Creates a proxy instance using one of the make() convenience functions of the proxy class. Notice that the previously determined correct transport is passed as a parameter. |
//4 | Checks that creation of the proxy has succeeded. |
//5 | Invokes the service operation method for request-response. |
//6 | Invokes the service operation method for one-way messages. Notice that the listener is sent as a parameter so the invocation function can obtain information on the transport, host, and port being used. |
//7 | //7-//9These lines of code determine when, if ever, notification has ended, which allows the listerner to be shut down and the client to exit. The first line obtains the handle class of the notification implementation. The next line obtains the body for that handle. The third line says to continue processing until the call to finalMessageReceived() returns true. |
//10 | Shuts down the listener for notification messages. |
Three extra classes are generated on the client side to support the notification pattern.
WeatherSummaryNotificationSkeleton: The skeleton that receives and handles the message, in this case on the client. Contains the notification operation methods.
WeatherSummaryNotificationBase: The base class for the client-side notification implementation. Similar to WeatherSummaryBase, except on the client side.
WeatherSummaryNotificationImp: Derives from WeatherSummarytNotificationBase. A sample implementation that you may use and edit.
In addition, three client-specific configuration files are used:
client-transports.xml (which is not actually generated but copied from the <installdir>\conf\webservice directory)
client-handlers.xml
client-objects.xml
The code below is an excerpt from WeatherSummaryNotificationImp.cpp. This code receives the notification operation method from the server.
RWSF_DEFINE_STATIC_MESSAGE_HANDLER("WeatherSummaryNotificationImp", WeatherSummaryNotificationImp) //1 bool WeatherSummaryNotificationImp::finalMessageReceived() //2 { return done_; } void WeatherSummaryNotificationImp::weatherNotification (rwsf::CallInfo& context, const wsx::WeatherSummary& weatherData_in) { std::cout << "Marshaling WeatherSummary received from server" << std::endl << "as a notification message:" << std::endl << std::endl; std::cout << weatherData_in.marshal() << std::endl //3 << std::endl; done_ = true; //4 }
//1 | Registers the client notification implementation as a message handler. |
//2 | Function polled by line //9 of the client implementation code. |
//3 | Marshals the WeatherSummary object received in the notification message as an XML document. |
//4 | Sets done_ equal to true so that finalMessageReceived() returns true, causing the client to shut down the listener and exit. |
In this example, after the server sends its last notification, it immediately sends a renewal request to the client as a solicit-response operation. As discussed earlier, a working application would use some other event to trigger this message, but for the purposes of this example, the code has been simplified.
The code below is taken from WeatherSummaryNotificationImp.cpp. This code receives the message from the server and sends a response, using the solicit-response service operation method weatherUpdateRenew().
std::string WeatherSummaryNotificationImp::weatherUpdateRenew(rwsf::CallInfo& callInfo, const std::string& RenewResponse_in) //1 { std::cout << "Printing renewal notice received from server:" << std::endl << std::endl; std::cout << " \"" << RenewResponse_in << "\"" << std::endl << std::endl; std::string response = std::string("Absolutely! Sign me up for another month."); //2 std::cout << "Sending the following renewal response:" << std::endl << std::endl; std::cout << " \"" << response << "\"" << std::endl << std::endl; done_ = true; //3 return response;
//1 | Receives the renewal request from server. |
//2 | Creates a string response to contain the answer, and prints a status message. |
//3 | Sets done_ to true to shut down the listener, and returns response. |
This is a part of the client-transports.xml file that supports client notification.
<rwsf:listener name="HTTP" uri="http://schemas.xmlsoap.org/soap/listener/http" scheme="http" default="true" class="rwsf_webservice.createHttpMessageListener"> <rwsf:property name="auto-start" value="false"/> <!-- When host and port listener properties are absent, they will be auto-configured to the machine's host name and first available port. The machine will need to be configured for lookup on the network under its host name. --> <rwsf:property name="host" value="localhost"/> <!-- <rwsf:property name="port" value="9000"/> --> <rwsf:property name="request-backlog" value="5"/> <rwsf:property name="request-timeout" value="60000"/> <rwsf:property name="request-buffersize" value="4096"/> <rwsf:property name="keep-alive" value="true"/> </rwsf:listener>
In the highlighted line above, note that the "port" properties are commented out, allowing the listener location for the client notification to be dynamically assigned. If you do not want a dynamic assignment, uncomment these properties and enter values for port. For more information on assigning a listener a location, either through dynamic assignment or specifically, see Section 11.4, "Autoconfiguring Listeners."
©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.