Developing Web Services in C++, Part II

n this article, I will demonstrate how to write a SOAP service in C++. The service is simple and consists of two operations; one to convert Fahrenheit to Celsius, the other to covert Celsius to Fahrenheit. I will also show the accompanying client code, but will not discuss the implementation as it differs only slightly from the Google client discussed in Part I. Part I also gave a brief introduction to SOAP and WSDL, with links to other resources for a more in-depth discussion. Part II assumes a basic understanding of these standards.

Resources You’ll Need
Before you can begin writing any source code, it is necessary to get your house in order. You’ll need to get a copy of Systinet’s WASP Server for C++ that is appropriate for your environment, and Systinet’s WASP Server for C++ Companion Tools.

WASP is available for a variety of operating systems and compilers as a free download from Systinet. It is also available for free deployment on single CPU machines. Purchasing a license is only necessary for deployment on multi-CPU hardware. Obviously, you will also need a C++ compiler. In my case, I am using Red Hat Linux 7.3 and gcc 2.96. You’ll need a 1.3 or better JVM as well, if you don’t already have one.

Install both WASP products according to the documentation. Set the WASPC_HOME environment variable to point to the WASP Server for C++ installation directory. It is also helpful to add the bin directories for both products to your PATH, and to create directories for your new service and client. Translate the code below as necessary for your environment.

$ export WASPC_HOME=/path/to/waspforc++$ export PATH=$PATH:/path/to/waspforc++/bin:/path/to/companiontools/bin$ mkdir service client$ cd service

Generating the WSDL Document
One of the tricks to creating a Web service is deciding where to start. There are two principal components to a Web service: the service itself and the WSDL document that describes it. It is helpful to have the WSDL in hand when developing a service, as you can then use the WASP wsdlc utility to auto-generate the client stubs and service skeletons. Conversely, since WSDL documents are a bit complicated, it would also be nice not to have to create the WSDL manually.

Fortunately, there is a solution to this problem. While the WASP for C++ product does not have a means of generating a WSDL from a C or C++ source or object file, it is possible to generate a WSDL from a Java class file. This is why you installed the Companion Tools. It contains, among other things, the Java2WSDL utility.

The first thing you want to do is create an extremely simple Java interface for your service. As noted, this service will consist of two operations for converting back and forth between Fahrenheit and Celsius. Call them ftoc() and ctof(). Each should take a single floating point number as an argument and return a floating point number as a response. Decide now that the endpoint for this service (that is, its URI) will be /tempconverterservice/. Knowing this, you can move on to writing your Java interface.

interface Tempconverter {  double ftoc (double fahr);  double ctof (double cel);}

Save this source as Tempconverter.java and compile it.

$ javac Tempconverter.java

This generates a class file that you can use to create the WSDL document. To do this, call the Java2WSDL.sh utility bundled with WASP for Java, and supply a few flags. The following command has been formatted for readability:

Java2WSDL.sh   --service-mapping Tempconverter=Tempconverter   --class-mapping       Tempconverter=http://localhost:6070/tempconverterservice   --no-java-mapping-extension   --sbs rpc   --ses encoded   Tempconverter

An explanation of these flags may be useful:

  • –service-mapping Tempconverter=Tempconverter: Causes the WSDL name attribute to be set to the string on the right for the Java class named on the left, instead of using the default name “JavaService.”
  • –class-mapping Tempconverter=http://localhost:6070/tempconverterservice: Causes the WSDL service/port/address element for the class on the left to be set to the URI on the right. In this case, port 6070 is the default HTTP port for WASP for C++. tempconverterservice is the arbitrary name decided on earlier for this particular service (I’ll refer to it again later).
  • –no-java-mapping-extension: Removes from the WSDL special constructs for handling overloaded methods in Java.
  • –sbs rpc: Sets the SOAP binding style to “rpc”, instead of “document.”
  • –ses encoded: Sets the SOAP encoding style to be “SOAP encoded” instead of “literal.”

By default, Java2WSDL creates a file called Definitions.wsdl. For consistency’s sake, rename this file to Tempconverter.wsdl.

$ mv Definitions.wsdl Tempconverter.wsdl

Creating the Service
Now that you have the WSDL document, you can use it to generate the bulk of your client and service source code. As seen in the previous article, wsdlc is the utility used to compile a WSDL document into C++ source.

wsdlc Tempconverter.wsdl Tempconverter

The second argument to wsldc is a project name. That is, a string that wsdlc uses when naming the various files it creates. The output of this command will be a series of C++ source files that supports the development of both the client and the service. These files represent client stubs and server skeletons. As a developer, you are chiefly interested in the header files. You can remain, thankfully, ignorant of the others.

Once WASP has generated all the difficult code, you only need to do two things: implement the service and stitch everything together. First, declare the service in a distinct header file. This class declaration should be derived from the TempconverterImpl skeleton class that was created by wsdlc.

#include "tempconvImpl.h"class TempconverterService : public TempconvertrImpl {  public:    double ftoc (double);    double ctof (double);};

That’s it. Save this file as TempconverterService.h. Next, implement the TempconverterService class.

#include "TempconverterService.h"double TempconverterService::ftoc (double fahr) {  return ((fahr - 32) * 5) / 9;}  double TempconverterService::ctof (double cel) {  return ((cel * 9) / 5) + 32;}

That’s pretty simple too. Save this file as TempconverterService.cpp.

Implementing the Server
It only remains to take this service and compile it with some boilerplate infrastructure code. Keep in mind that, unlike SOAP servers available for more dynamic languages like Java and C#, C++ does not allow you to deploy object files to a distinct server process. It is therefore necessary to link your service with the WASP server code in order to create a SOAP-enabled executable. It stands to reason that the server code will then need to be made aware of the service. Thus, your last bit of server-side source code (shown in Listing 1) registers your service with WASP, starts up the WASP environment, and waits forever.

After including the requisite headers, the first thing this code does is register your service with WASP. When expanded, the WASP_FACTORY_DEFINE macro declares a factory function that returns a new instance of your class. In the main() body, create an array of these function pointers with the help of a few more macros. Under the covers, each array element holds a struct containing the name of your service and the pointer to the factory function that instantiates it.

In this case, you have just one service, but as you create more services in the future, you can simply add them to this list. No other code needs changing. So, if you were to later create a service, say, a currency converter, yentodollar, and that service was defined in its own class called CurrconverterService, you would change the above code as follows, and recompile.

WASP_FACTORY_DEFINE (TempconverterService);WASP_FACTORY_DEFINE (CurrconverterService);int main (int, char **) {    WASP_FactoryDefinition serviceFactory[]={        WASP_FACTORY_ENTRY (TempconverterService),        WASP_FACTORY_ENTRY (CurrconverterService),        WASP_FACTORY_END ()    };     }

With this infrastructure in place, all you need to do now is initialize WASP, register your factories with the WASP super factory, and start her up. Remember to wrap the SOAP engine up with some exception handling. Also remember to terminate the WASP server politely. Save the server code as tcserver.cpp. Then, compile and link everything together.

$ g++ -o tcserver *.cpp I/usr/local/waspc/include  /usr/local/waspc/lib/libwasp.so

Configuring the Server
You may be tempted to start your new server, but don’t?it won’t work. If you think about things for a second, you’ll realize there’s a couple of loose threads. For instance, the server does not know that the Tempconverter.wsdl document is related to the TempconverterService service. Nor does it know what URL should cause the TempconverterService service to be instantiated. Fortunately, the answers to these questions do not require any further coding. This information (and more, if necessary) can be setup in a simple XML configuration file.

            sep:wsdl="Tempconverter.wsdl"        sep:url="/tempconverterservice/">                    svci:class="TempconverterService"         svci:name="tempconverter"/>

After some initial (boilerplate) namespace declarations and server configuration, you arrive at the elements that control the service endpoint. Of the components in the attribute list, the ones you’re concerned with are the wsdl and url attributes. The wsdl attribute describes the path to the service’s WSDL document relative to the start-up directory of the server. The url attribute, obviously, specifies the URL of the service relative to the root of the HTTP server.

The use of the tag simply names this endpoint so that it can later be associated with a service instance as known to your server, i.e. your object code. This is done with the element. Here, you can see that name refers back to the serviceEndpoint/instance ref attribute, and class refers to the name of the class as specified in the WASP_FACTORY_DEFINE macro of your server. Save this file as config.xml (as dictated by the WASP_Runtime::serverStart() function).

With all the components in place, you can start your server.

$ ./tcserver&

Creating the Client
Servers aren’t too interesting without their clients, so you’ll want to develop a SOAP client to exercise your new service. I recommend creating the client code in the separate client directory you made earlier. If you do this, you’ll want to copy over a few of the files that wsdlc generated.

$ cp Tempconverter.h Tempconverter.cpp TempconverterStructs.h TempconverterStucts.cpp ../client

Listing 2 shows a sample client that is even simpler than the Google client presented in Part I. Save this client code as tcclient.cpp in the client directory, then compile and link it.

$ g++ -o tcclient *.cpp I/usr/local/waspc/include  /usr/local/waspc/lib/libwasp.so

When you run this client, you should see the following:

$ ./tcclient70.00 degrees Fahrenheit = 21.11 degrees Celsius70.00 degrees Celsius = 158.00 degrees Celsius

That’s how easy it is to develop Web services in C++ or to expose existing C/C++ code as Web services!

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

Recent Articles: