Web Service Development Guide : PART II Client and Server Options : Chapter 7 Developing Clients : Developing and Running the Example
Developing and Running the Example
 
Service Description
The client created in this example uses the WSDL provided in the file DayOfWeek.wsdl located in the examples\webservices\DayOfWeek directory of your HydraExpress distribution. The WSDL file defines a single operation, GetDayofWeek, located at a port named DayOfWeekPortType. Since this example focuses on the various options available to a client rather than designing an implementation, the service is deliberately simple. The operation receives a message containing an XML Schema date part. The operation returns a message containing a string that holds the day of the week on the date provided.
Invoking the Generator
Make sure that you have set up your environment as described in Chapter 2, “Setup,” in the HydraExpress User Guide.
Open a command prompt and change to the examples\webservices\DayOfWeek directory under the RWSF_HOME directory, as shown below:
Windows
cd %RWSF_HOME%\examples\webservices\DayOfWeek
UNIX/Linux
cd $RWSF_HOME/examples/webservices/DayOfWeek
In addition to the DayofWeek WSDL and the provided implementation files, this directory contains a HydraExpress project file, example-project.xml. This file provides custom configuration information to the generator. (For more information on the HydraExpress project file, see Chapter 21, “The Project File and Directory Structure.”
If you open this file, you’ll note that it identifies a value for the project-name attribute and a files element that references the DayofWeek.wsdl, so there is no need to include these items on the command line if this file is provided to the generator.
Run rwsfgen with example-project.xml as the sole argument, as follows:
 
prompt> rwsfgen example-project.xml
The above invocation is exactly the same as the following:
 
prompt> rwsfgen -projectname DayOfWeekExample -whitespace DayofWeek.wsdl
NOTE >> The two required options above are -projectname and the name of the file to parse. The project name is required, and may be entered directly into a HydraExpress project file or on the command line. Note that the name may not include spaces, as HydraExpress supports only file paths without spaces.
Since this chapter is primarily interested in creating a client to access the service, we could also choose to generate only a client, using an additional argument -noserver as follows:
prompt> rwsfgen -noserver example-project.xml
Since we will be using this example to also discuss server-side options in Chapter 8 , we’ll build both a client and server.
For full information on the various code generation options, see “Generator Options.”
Generated Files
HydraExpress generates server and client-side code, sample applications, configuration files, makefiles, MSVC project files, and documentation into a directory DayOfWeekExample, using the options included in the example-project.xml file. (HydraExpress supports only file paths without spaces, so when naming your project, do not include spaces, since HydraExpress uses the project name as a basis for the name of the code generation directory.)
The project files are generated into several subdirectories, according to HydraExpress’ standard code generation directory hierarchy. For more information on the directory structure, see “The Generated Project Directory.”
NOTE >> Files that are overwrite-protected are generated with a “.sample” extension if a file exists in the code generation directory of the same name. See “Overwrite Protection.”
Table 4 – Files generated for DayofWeek example 
Directory
Files
Description
DayOfWeekExample/
DayOfWeekExample.xml
The project file, named according to the project name, defining all project elements (options, configurations, schemas, WSDLs, etc.). See “The Project File” .
 
makefile
makefile_debug
Project-level makefiles. Calls all makefiles in subdirectories. Overwrite-protected.
 
makefile.include
Include makefile. Contains all makefile compiler and linker options. Edits to this file cascade down to all makefiles. Overwrite-protected.
 
DayOfWeekExample.sln
MSVC solution file. Generated for Windows platforms.
 
deployDebug.bat
deployRelease.bat
Convenience debug and release deployment batch files generated for use with MSVC projects.
    app/
makefile
makefile_debug
Makefiles. Builds contents of app/ directory.
Overwrite-protected.
        /client
DayOfWeekPortClient.cpp
Sample client implementation using the client proxy. Overwrite-protected.
 
makefile
makefile_debug
Makefiles. Builds contents of app/client directory. Overwrite-protected.
 
DayOfWeekPortClient.vcproj
MSVC project file generated for Windows platforms.
        /server
DayOfWeekPortTypeImp.cpp
DayOfWeekPortTypeImp.h
Sample service implementation and header files. Overwrite-protected.
 
makefile
makefile_debug
Makefiles. Builds contents of app/server directory. Overwrite-protected.
 
DayOfWeekPortServiceSample.vcproj
MSVC project file generated for Windows platforms.
    codegen/
makefile
makefile_debug
Makefiles. Builds contents of codegen/ directory. Overwrite-protected.
        /client
DayofWeekBindingProxy.cpp
Implementation file for the generated client proxy class. Defines the methods for the operations defined in the WSDL file.
 
makefile
makefile_debug
Makefiles. Builds contents of codegen/client directory. Overwrite-protected.
 
DayOfWeekBindingClientLibrary.vcproj
MSVC project file generated for Windows platforms.
        /server
DayOfWeekBindingSkeleton.cpp
DayOfWeekPortTypeBase.cpp
Implementation files for the class that handles messaging and the base class for the server-side implementation.
 
makefile
makefile_debug
Makefiles. Builds contents of codegen/server directory. Overwrite-protected.
 
DayOfWeekPortService.vcproj
MSVC project file generated for Windows platforms.
    conf/
transports.xml
client-transports.xml
Configuration files for:
1. Server and client transports
 
dayofweek_handlers.xml
2. Server-side handlers to support handler chaining and configuration
 
client-handlers.xml
3. Client-side handlers containing default client logger
 
dayofweek_objects.xml
4. All named objects required by this service. These are registered and created when the Agent is started.
 
dayofweek_web.xml
5. Service Descriptor XML file for the service
 
makefile
makefile_debug
Deployment makefiles. Overwrite-protected.
    docs/
index.html
The generated documentation. Use index.html as the entry point to all docs contained in the docs subdirectories.
    include/
 
Header files, as follows:
        /DayOfWeekExample
Subdirectory by project name
 
DayOfWeekBindingProxy.h
1. Client proxy class
 
DayOfWeekBindingSkeleton.h
2. Class that handles messaging
 
DayOfWeekPortTypeBase.h
3. Base class for the server-side implementation
Note that the directories bin and lib are empty at code generation, but will include the .exe and .dll files, or the .lib or .so files after the project build, depending on your platform.
Using the Client Proxy
The proxy provides a set of five methods for each operation specified in the WSDL description. One pair of methods implements synchronous behavior; the remaining three methods implement asynchronous behavior. To send a message to the Web service, construct an instance of the proxy and invoke either the synchronous or asynchronous method(s) that correspond to the operation. The method returns the values defined in the WSDL file for the response message. (Appendix A describes type mappings for these values.)
The generated sample client implementation uses one of the synchronous methods for all operations, but you can easily change the implementation to create an asynchronous client. The asynchronous <operation-name>Start() method launches a thread for the operation so that the client can continue other processes while waiting for the server to respond. For more information on the client’s asynchronous features, see “The Asynchronous Client API.”
The generated header file for the client proxy contains the signatures for the generated service operation methods. The names of the methods that implement the operations in the WSDL file match the WSDL’s operation names, with the first letter of the name converted to lower case. Parameter names match the part names in the WSDL file, with an underscore and the direction of the part appended to the name. For example, a part named tickerSymbol which is only used for input will have the name tickerSymbol_in, while an inout part named targetStruct will have the name targetStruct_inout. For details on naming conventions, see Appendix A .
Looking at the Code
The sample below shows the public interface for the client proxy, DayOfWeekBindingProxy.h located in the DayofWeekExample\include\DayofWeekExample directory.
The sample is reformatted slightly to fit on the page, and omits include statements, comments, and private members:
 
class DayOfWeekBindingProxyImp;
 
class DAYOFWEEKBINDINGCLIENTLIBRARY_DECLSPEC
DayOfWeekBindingProxy : public rwsf::Client //1
{
public:
 
static DayOfWeekBindingProxy make(const std::string& location = //2
"http://localhost:8090/dayofweek/DayOfWeek");
static DayOfWeekBindingProxy make(const rwsf::Transport& transport); //3
static DayOfWeekBindingProxy make(DayOfWeekBindingProxyImp *impl); //4
 
DayOfWeekBindingProxy(); //5
DayOfWeekBindingProxy(const DayOfWeekBindingProxy& proxy);
~DayOfWeekBindingProxy();
 
DayOfWeekBindingProxy& operator=(const DayOfWeekBindingProxy& proxy); //6
 
void setTransportProperty(const std::string& property, //7
const std::string& value);
 
std::string getDayOfWeek(const std::string& date_in); //8
std::string getDayOfWeek(rwsf::CallInfo& callInfo,
const std::string& date_in);
rwsf::AsyncHandle getDayOfWeekStart(const std::string& date_in); //9
rwsf::AsyncHandle getDayOfWeekStart(rwsf::CallInfo& info,
const std::string& date_in);
std::string getDayOfWeekEnd(rwsf::AsyncHandle& handle); //10
 
 
//1 Derives from base class rwsf::Client
//2 The proxy includes three make() convenience methods for obtaining an instance of the proxy.
The first returns a handle to the proxy based on a location parameter specifying the URL of the service. By default, the location is the URL specified in the WSDL file.
//3 Returns a handle to the proxy based on a transport parameter specifying the transport for the service. This method is used if a transport type is passed in on the command line, or if the client implementation needs to select a transport dynamically at runtime. Use this method to use a transport other than the default specified in the WSDL.
//4 Returns a handle to the DayOfWeekBindingProxyImp body implementation passed as a parameter. (This method is used internally.)
//5 Default constructor, copy constructor, destructor.
//6 Assignment operator.
//7 Sets a property in the transport associated with this client proxy.
//8 Declares the first of the two synchronous getDayOfWeek() methods. These methods take a string containing the date, and return the day of week for that date.
The second method in the pair differs only in explicitly passing a rwsf::CallInfo object. Use this method if you need to add your own SOAP or transport headers to the message (see Chapter 15, “SOAP and Transport Headers.”), or you want to add some additional message handlers for the outgoing message (see Chapter 14, “SOAP Message Handlers.”).
//9 Declares the first of the two asynchronous getDayOfWeek() methods. These methods return a handle to an object that can be used to check on whether the operation has completed, and to retrieve the data if it has. See “The Asynchronous Client API” for an explanation of this interface.
The second method with the rwsf::CallInfo parameter plays the same role as the corresponding synchronous method.
//10 Declares the end method for an asynchronous process, which is used in retrieving the data.
Using the Client Proxy
Use the proxy to implement your client application. HydraExpress creates a sample implementation you may use as a framework. This section discusses how to implement the proxy for a synchronous operation. For information on the asynchronous operation, see “The Asynchronous Client API.”
First, we’ll look at the generated class, and then we’ll see how we can easily edit it to implement the getDayofWeek method.
The generated sample implementation below shows the DayOfWeekPortClient.cpp file created by the rwsfgen program and located in the DayOfWeekExample\app\client directory. The sample is not a class, but a source file that includes the proxy header file and a main() function that invokes the proxy’s methods.
 
#include "DayOfWeekExample/DayOfWeekBindingProxy.h" //1
#include <iostream>
 
#include <include/webservice/DefaultLogger.h>
#include <include/webservice/HandlerManager.h>
#include <include/webservice/MessageHandler.h>
#include <include/webservice/transport/TransportManager.h>
 
void invoke_DayOfWeek(DayOfWeekBindingProxy& proxy) //2
{
std::string date_in;
std::string dayOfWeek_ret;
rwsf::CallInfo callInfo;
 
try {
dayOfWeek_ret = proxy.getDayOfWeek(callInfo, date_in);
} catch(const rwsf::SoapFaultException& e) {
std::cout << "Fault Code: " << e.getFault().getFaultcode().asString()
<< std::endl;
std::cout << "Fault String: " << e.getFault().getFaultstring() << std::endl;
}
}
 
void logError(std::string error, std::string detail) {
rwsf::HandlerManager::invokeLogger(error + ": " + detail,
rwsf::CallInfo::Error);
}
 
using rwsf::DefaultLogger;
RWSF_DEFINE_STATIC_MESSAGE_HANDLER("RWSFClientLogger",DefaultLogger)
 
int main(int argc, char* argv[])
{
 
std::string location; //3
 
if (argc < 2) {
location = "http://localhost:8090/dayofweek/DayOfWeek";
}
else if (argv[1][0]=='-' && argv[1][1]=='a') {
location = "http://localhost:8013/dayofweek/DayOfWeek";
}
else if (argv[1][0]=='-' && argv[1][1]=='l') {
location = "http://localhost:8090/dayofweek/DayOfWeek";
}
else {
location = argv[1];
}
try {
// initialize config files //4
rwsf::NamingContext::loadGlobal("../conf/client-objects.xml");
rwsf::HandlerManager::loadConfiguration("../conf/client-handlers.xml" );
rwsf::TransportManager::initialize("../conf/client-transports.xml" );
 
DayOfWeekBindingProxy proxy =
DayOfWeekBindingProxy::make(location); //5
 
if(!proxy.isValid()) {
std::cerr << "Unable to create proxy. " << "\nExiting" <<
std::endl;
return 1;
}
invoke_getDayOfWeek(proxy); //6
 
} catch (const rwsf::Exception& x) { //7
std::cerr << "rwsf::Exception thrown: "
<< x.what() << std::endl;
return 1;
} catch(const std::exception& x) {
std::cerr << "std::exception thrown:" << std::endl
<< x.what() << std::endl;
return 1;
} catch(...) {
std::cerr << "Unknown exception thrown" << std::endl;
return 1;
}
return 0;
}
//1 Includes necessary header files.
//2 Sample function that invokes the getDayOfWeek operation. The function takes a reference to a DayOfWeekPortClient proxy, creates standard string instances of the input and output variables as well as an instance of rwsf::CallInfo, and calls the getDayOfWeek operation on the proxy. The client generator implements one sample invocation method for each operation. The method uses the synchronous proxy method that takes an explicit rwsf::CallInfo parameter. The program provides a try block around the code that calls methods on the proxy. The proxy indicates errors by throwing a rwsf::SoapFaultException derived from rwsf::Exception, and the server writes a log message by default. Note that the generator makes no attempt to provide valid input for the operation.
//3 The main() function first determines the location for the service through a combination of information from the WSDL and possible command line options and arguments.
If no command line argument is used when invoking the client, the default is the location specified in the WSDL. (In this case, the location points to the simulated DayOfWeek service that ships with HydraExpress.)
//4 Registers the service handlers, naming context, and transports that are available for this message.
//5 Obtains a client proxy and ensures that it is valid.
//6 Calls the invoke_DayOfWeek() method with the proxy.
//7 Catch blocks for exceptions that might be thrown by this call. The exceptions shown should always be caught. If you generate code with the ‑SourcePro option, which uses Rogue Wave SourcePro C++ datatypes, you should also catch exceptions derived from RWxmsg.
To create a working client, simply implement the invoke_getDayOfWeek method, replacing the generated code with your own.
In this case we will use the implemented client application DayOfWeekPortClient.cpp provided in the directory <installdir>\examples\webservices\DayofWeek.
The code below shows how the shipped file implements the service operation method invoke_getDayofWeek().
 
void invoke_getDayOfWeek(DayOfWeekBindingProxy& proxy)
{
std::string date_in;
std::cout << "Enter date: " << std::flush;
std::cin >> date_in;
rwsf::DateTime dt(date_in, rwsf::DateTime::setDate);
if (!dt.isValid()) { std::cout << "Error: Invalid date" << std::endl;
return;
}
else {
rwsf::DateTime today(rwsf::DateTime::setCurrentTime);
std::string toBe;
std::string dayOfWeek_ret;
if (date_in < today) {
toBe = " was a ";
} else if (date_in == today) {
toBe = " is a ";
} else {
toBe = " will be a ";
}
rwsf::CallInfo info;
 
try {
dayOfWeek_ret = proxy.getDayOfWeek(info, date_in);
std::cout << date_in << toBe << dayOfWeek_ret << std::endl;
} catch(const rwsf::SoapFaultException& e) {
std::cout << "Fault Code: " << e.getFault().getFaultcode().asString()
<< std::endl;
std::cout << "Fault String: " << e.getFault().getFaultstring()
<< std::endl;
}
}
}
...
 
Class rwsf::DateTime correctly parses many common date formats. The client uses this class to simplify the process of converting input from the console into the ISO8601 format that the schema requires.
Compiling and Deploying the Client
At this point the client is implemented and ready to compile. The service now needs to be implemented; then both can be built and the service can be deployed to the HydraExpress Agent. Chapter 8 discusses how to implement the service, and “Compiling the Service” discusses building the client and service, and deploying the service.