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;
}
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;
}
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
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;
}
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.”