omewhere north of a million. That, last time you counted, is the number of lines of C and C++ code keeping your organization running. And that is the number that runs through your head as you examine the literature surrounding Web services. The reason, of course, is that the bulk of information available on the subject is focused on developing Web services with Java, C#, Visual Basic, and other languages which, frankly, doesn’t do you much good at all. Web services promises to make it easy to integrate C++ legacy systems with pretty much anything else, except for one catch: theres precious few products that support creating and deploying C++ Web services. This article provides an answer to this problem.
This article demonstrates how to write a SOAP client in C++. The client will exercise Google’s SOAP-enabled search API, a service that accepts and answers search queries.
Resources for this Article
Before you 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 Google’s Developer’s Kit. Both are free.
- WASP Server for C++
WASP is available for a variety of operating systems and compilers as a free download from Systinet’s Web site. Even more appealing is that WASP is available for free deployment on single CPU machines. Purchasing a license is only necessary for deployment on multi-CPU hardware.
- Google Developer’s Kit
You will need to download Google’s developer’s kit, although all youll really use from that is the WSDL file that describes their services and the license key. It takes only moments and costs you nothing to register with Google. It goes without saying that you’ll need a C++ compiler. In my case, I am using Red Hat Linux 7.3 and gcc 2.96.
A Brief Introduction to Web Services
Unfortunately, this is not the place for an in-depth discussion of SOAP and WSDL. If you are unfamiliar with both, we recommend looking at the whitepaper, “Introduction to Web Services,” or you can check out the Related Resources in the left-hand sidebar.
Briefly, SOAP (Simple Object Access Protocol) is the core protocol underlying Web services. It defines an XML envelope in which a developer can place an XML payload. This envelope can be delivered to a service over a variety of transports, such as HTTP and SMTP. Further, SOAP, WSDL, and the development environment you use help define a mechanism for delivering this payload in such a way as to mimic remote procedure calls (RPC). SOAP also specifies a means of encoding data in a language neutral format. WSDL (Web Services Description Language) is the key to SOAP interoperability. It defines the “what, how, and where” of your Web services. Technically, it’s a published, static XML document that describes the data types your service consumes and returns (like arrays and structures), the allowed input and output messages, the various services available, and the URLs where they can be found.
Google has made a series of SOAP services that, among other things, provide access to Google’s search functionality. Technically, thats all you need to know up front. Everything else you can glean from their WSDL document as you proceed with this tutorial.
|Editor’s Note: Peter Lacey is an engineer for Systinet Corp.
Creating a SOAP Client
With WSDL in hand, developing a SOAP client for a service is simplicity itself. Via a process that should look familiar to CORBA developers, use Google’s WSDL document to create a SOAP Remote Procedure Call client that calls the doGoogleSearch service. This service, among others, is denoted in the
As a programmer, however, it is not strictly necessary to understand the entirety of the WSDL document. It is only necessary to convert it into C++ code. Assuming you have installed WASP according to the documentation, exported WASPC_HOME to your environment, and added the waspc/bin directory to your PATH, you can simply run the wsdlc utility which converts a WSDL document to C++ client stubs and server skeletons. I recommend that you create a new directory for the development of your client, and copy the GoogleSearch.wsdl document into it.
$ mkdir google_client$ cp [googleapi]/GoogleSearch.wsdl google_client$ cd google_client$ wsldc GoogleSearch.wsdl GoogleSearch
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 is a series of C++ source files that support you in developing both a client and a service. Since the service already exists, you won’t need the server oriented files, and can safely delete them (you’ll encounter them again in the second installment of this series).
$ rm GoogleSearchImpl.cpp GoogleSearchImpl.h
Of the remaining files, you are chiefly interested only in GoogleSearch.h and GoogleSearchStructs.h. The others become important later, as they implement the base classes you’ll extend and the various helper classes and methods that WASP will use.
Now that WASP has generated all the difficult code, simply write some code to exercise it. The complete source code listing can be found here. In the following sections, I’ll describe it.
Initializing the Environment
The listing begins with a number of boilerplate #includes, the last of which is one of the header files output by the wsdlc utility. A quick examination of the GoogleSearch.h file shows that WASP has converted the message (i.e., the parameters) of the doGoogleSearch service to a series of native C++ types and WASP-provided wrapper types. The first thing you want to do in this program is create and initialize these arguments:
WASP_VString key = ;WASP_VString query = ;int start = 0;int maxResults = 10;bool filter = false;
The WASP_VString class is a wrapper around an ordinary C/C++ char *, primarily offered to allow transcoding back and forth from ASCII to the UTF-8 and UTF-16 character encoding used in SOAP. It should be used for all string data types passed back and forth between SOAP clients and servers.
The call to WASP_Runtime::clientInitialize() is a mandatory call that, obviously, initializes and configures the client environment, and should be called before any SOAP-oriented code is executed. It differs from the WASP_Runtime::clientStart() function a few lines down in that clientInitialize() performs core functions like initializing threads and factories, while clientStart() initializes the transport layer, the serialization mechanism, transcoders, and such, all of which can be manipulated. Note that clientStart() requires a configuration filename; the default of conf/client.xml, found in the WASP_HOME/share/waspc directory, is sufficient for our needs.
The first interesting thing here is the TRY macro. The TRY macro and its related CATCH, AND_CATCH_ALL, and STOP_CATCH_ALL macros are provided by WASP so that exception handling may be supported on all platforms, including Windows CE and those platforms unable to use exceptions in shared libraries.
Next, initialize the two most important parameters to the doGoogleSearch service; the activation key and the query itself. For the code listing, I have starred out my key, and you will need to supply the one you received from Google.
Calling the SOAP Web Service
Finally, it’s time to call the Google search service.
GoogleSearchPort gsp;GoogleSearchResult *ret = gsp.doGoogleSearch(key, query, );
The WSDL compiler creates a stub class for every
Your code instantiates an instance of this class, and then calls the service (aka method) you want. This is the beauty of Web services?the ability to call a service written in an unknown language, hosted on a foreign computer, as if it were local to your own machine!
A review of the WSDL shows that the doGoogleSearch() service has an
The public member elements of the GoogleSearchResult are fairly straightforward. This implementation concerns itself only with the searchQuery and resultElements members. searchQuery contains the original query as returned from Google, and is a WASP_VString. Print it out by calling its transcode() method. This method converts the return value from Unicode, returning a simple char *. Note that transcode() dynamically allocates its return value, and that value will need to be deleted. WASP_VString makes a variety of transcoders available, this particular transcoder is specific to the ASCII character set.
char *sq = ret->searchQuery.transcode();printf("You queried Google on %s
", sq);delete sq;
The resultElements member is interesting. doGoogleSearch() returns up to ten results for each query. However, the results are not simple strings, but are complex types themselves, containing such things as a snippet of the found text and the URL where the document resides. As dictated by the WSDL, WASP has represented this complex type as a ResultElement structure, and has placed these ResultElement structures in a ResultElementArray class.
The ResultElementArray class implements another WASP wrapper class, WASP_AbstractArray. For now, it is only important to know that you can get a reference to a native C++ array via the ResultElementArray’s array member and the array size from its length member. With this information in hand you can loop through the result set and print out the information of interest.
ResultElementArray *results = ret->resultElements;ResultElement **i_results = results->array;int num_results = results->length;printf(Your query has returned %d results
, num_results);for (int i = 0; i snippet->transcode(); char *url = i_results[i]->URL->transcode(); printf("%d. %s
", i+1, snippet, url); delete snippet; delete url;}
All that is left is to clean up. Deleting a complex type like a WASP_AbstractStructure or a WASP_AbstractArray involves instantiating a WASP_DeleteContext, and passing it into the object’s remove() method (there are variations). The DeleteContext is a helper object that aids the object in safely removing itself, and any other objects it may reference, from memory. Its primary value becomes evident where there are complex linkages among objects, as seen here where deleting only the GoogleSearchResult is enough to also delete the ResultElementArray and the ResultElements that array holds.
Finally, there is the exception handling code. Length restrictions prohibit an in-depth discussion on this topic. However, there is little new in this section, and the code can be treated as a template. It is worth noting, however, that the first CATCH block catches SOAP faults as returned from the Google server, while the AND_CATCH_ALL block handles all other exceptions.
Compile and Run
Compile your code as follows, assuming WASP is installed in /usr/local:
g++ -o MyGoogleSearch *.cpp -I/usr/local/waspc/include /usr/local/waspc/lib/libwasp.so
Run our client, ./MyGoogleSearch, and see what Google considers the (at least) top ten results for a search query. Of course, the results are HTML formatted, but that’s beyond your control.
Thats all there is to it. An interesting exercise might be to re-implement this code to exercise Google’s, simpler, doSpellingSuggestion service.
I hope this was useful. In the next article Ill explain how to write a C++ Web service.