Web Service Development Guide : PART III Working with Message Patterns : Chapter 10 Solicit-Response and Notification Services : Implementing the Client
Implementing the Client
One the client side, we need to send and receive weather updates, using the provided sample client implementation WeatherSummaryClient.cpp. It does the following:
Subscribes to the weather service through a call to a request-response endpoint.
Sends a weather update to the weather service as a one-way message.
Verifies to the server that it can be reached by implementing a solicit-response service endpoint.
Receives back a weather update from the service by implementing a service notification endpoint.
Unsubscribes to the weather service through a call to the request-response endpoint.
For each step, the code writes status information to standard out to verify that the client and server are behaving as expected.
Setting up a Listener
The first task during the client initialization is to set up a listener so the client can receive request messages from the server. Notification and solicit-response require the server to establish the connection and the client to be listening for it.
 
rwsf::MessageHandler service =
rwsf::HandlerManager::findHandler("WeatherSummaryNotificationService"); //1
 
rwsf::MessageListener listener; //2
if (transportName.empty()) {
listener = rwsf::TransportManager::findListenerByUrl(location);
} else {
listener = rwsf::TransportManager::findListener(transportName);
}
 
if (listener.isValid()) { //3
std::cout << "Starting Listener..." << std::endl << std::endl;
listener.setHandler(service);
listener.start();
}
else {
std::cerr << "Unable to find a listener of name: "
<< transportName << std::endl;
std::cerr << "Exiting." << std::endl;
return 1;
}
//1 Creates a service handler for the client. The handler WeatherSummaryNotificationService is defined in the generated file client-handlers.xml.
//2 Creates a listener object based on whether the information available is a transport name or a location.
//3 If the constructed listener is confirmed as valid, starts the listener, and registers the service handler.
The Request-Response Service Operation Call
As another part of client initialization, the subscribe()method sends location and transport information to the weather service so the client can receive weather updates via its listener.
 
// set up the service proxy
rwsf::Transport transport; //1
if (transportName.empty()) {
transport = rwsf::TransportManager::findTransportByUrl(location);
} else {
transport = rwsf::TransportManager::findTransport(transportName);
}
 
WeatherSummaryProxy proxy = WeatherSummaryProxy::make(transport); //2
 
// subscribe to the WeatherSummary service.
std::cout << "Subscribing to WeatherSummary service..." << std::endl;
std::string host = listener.getProperty("bound-ip"); //3
std::string port = listener.getProperty("bound-port");
std::string scheme = listener.getProperty("name");
bool status; //4
std::string msg;
proxy.subscribe(host, port, scheme, status, msg); //5
if (status) { //6
std::cout << "Subscription successful." << std::endl;
}
else {
std::cerr << "Subscription failed: " << msg << std::endl;
return 1;
}
//1 Using logic similar to that for creating the listener, determines the transport to be used in creating the proxy.
//2 Creates the proxy based on the transport object passed into the static make() method.
//3 Acquires server, port, and transport scheme information from the listener.
//4 Creates two objects to be passed in the service request that will hold the response data.
//5 Sends the subscribe request message.
//6 Prints out information about whether the subscription succeeded based on the status value returned by the request. The status value is based on the results of the verifySubscription() solicit-response operation on the server. This operation simply sends a request to the client, which responds true if the request is received. See “Implementing the Client-Side Notification and Solicit-Response Endpoints” for the code.
The client also implements an unsubscribe() method that is very similar to subscribe().
The One Way Operation Call
After subscribing to the notification service, the client implementation makes a one-way proxy call that sends updated local weather data to the weather service one-way service endpoint. It does not receive a response to this message because it is one way. However, after the message is sent it receives a notification message from the server since it previously subscribed to receive notifications.
 
wsx::WeatherSummary ws; //1
ws.setSky("overcast"); //2
ws.setTemp(54);
ws.setWindSpeed(12);
ws.setZipcode("97584");
proxy.weatherUpdate(ws); //3
//1 Creates a WeatherSummary object. This is a complex datatype defined in a schema in the WeatherSummary WSDL file. The HydraExpress XML Binding feature generates C++ classes that allow you easily to manipulate this data in your code.
//2 Populates the WeatherSummary object with some data.
//3 Uses the proxy to send this data to the weather service.
Implementing the Client-Side Notification and Solicit-Response Endpoints
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. Dispatches to the notification operation methods in the notification implementation.
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
client-handlers.xml
client-objects.xml
The code below is from WeatherSummaryNotificationImp.cpp. It shows both the weatherNotification() method that handles the notification message initiated by the server, and the endpoint implementation representing the client response to the server’s verifySubscription() solicit-response message.
 
RWSF_DEFINE_STATIC_MESSAGE_HANDLER("WeatherSummaryNotificationImp",
WeatherSummaryNotificationImp) //1
 
void
WeatherSummaryNotificationImp::weatherNotification(rwsf::CallInfo& callInfo,
const wsx::WeatherSummary& weatherData_in) //2
{
std::cout << "WEATHER UPDATE RECEIVED: " << std::endl
<< " zipcode = " << weatherData_in.getZipcode() << std::endl
<< " windSpeed = " << weatherData_in.getWindSpeed() << std::endl
<< " sky = " << weatherData_in.getSky() << std::endl
<< " temp = " << weatherData_in.getTemp() << std::endl
<< std::endl;
}
 
bool
WeatherSummaryNotificationImp::verifySubscription(rwsf::CallInfo& callInfo) //3
{
return true;
}
//1 Registers the client notification implementation as a message handler.
//2 Prints out the data in the incoming notification message from the server.
//3 Responds true to the server’s verifySubscription message to indicate that the client is reachable from the server. This is the implementation of the solicit-response message endpoint.
Transport Configuration File Supporting Notification
This is a part of the client-transports.xml file that configures a listener to support client notification and solicit-response endpoints.
<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 this line and enter the desired value for port. For more information on assigning a listener a location, either through dynamic assignment or specifically, see “Using an Autoconfigured Listener.”