Channels ▼
RSS

C++WSP: A C++ Web Services Platform

Source Code Accompanies This Article. Download It Now.


Apr03: C++WSP: A C++ Web Services Platform

Khushnood is a senior technical architect and Yassar is a technical specialist for Infosys Technologies (http://www.infosys.com/). They can be contacted at naqvik@infosys.com and yassars @infosys.com, respectively.


Web services offer an open standards alternative to proprietary middleware. However, enabling existing systems with web services requires both development and run-time support. To do it with C/C++-based systems, you typically have two options:

  • Java web services via Java requires JNI, which is not considered a robust means of integration.
  • .NET, which only works on Microsoft platforms.

In this article, we propose a third option—our C++ Web Services Platform (C++WSP). This native C/C++ approach is more elegant than JNI and does not have the platform limitations imposed by .NET. It is worth noting that there are similar commercially available tools such as the WASP suite of tools from Systinet (http://www.systinet.com/). In addition to supporting web services for C++, WASP also supports Java.

Web services are usually implemented via the XML-based Simple Object Access Protocol (SOAP). Since the most common SOAP transport binding is HTTP, SOAP messages travel as the body of an HTTP message. Figure 1 is a sample SOAP request enveloped in an HTTP request (reproduced from the SOAP 1.1 specification document), while Figure 2 is a sample SOAP response enveloped in an HTTP response. Clients invoke web services by posting the SOAP request enveloped in an HTTP request on to the URL of the web service. Typically, a Web Service Definition Language (WSDL) document describes the web service and is available for the client's use. There may also be a Universal Description, Discovery, and Integration (UDDI) registry, which lets clients get the URL and WSDL for the service.

C++WSP Overview

Figure 3 illustrates the three layers of C++WSP: The bottom layer is the Servlet Engine, the middle layer is the SOAP Handler, and the top layer contains the SOAP applications. The shaded boxes show the open-source components (gSOAP, Apache), while the white boxes show those developed to create the SOAP layer. The C/C++ SOAP applications are also shown as white boxes. Table 1 lists the important classes (and the corresponding source files) of the C++WSP implementation.

Our aim was to develop a SOAP Handler for C++ apps that can work with a web server. Of course, we could have done this by writing a web-server extension (Apache module) directly. However, we chose servlets because:

  • They make the SOAP layer more platform and web-server independent. The Servlet Engine is written to be platform and web-server independent.
  • This approach is more modular as the servlet layer takes care of dealing with HTTP parsing and hands it to the SOAP layer for the SOAP parsing.

  • Basing it on servlets lets you use the servlet configuration and context data for the SOAP application deployment.

We started by building a Servlet Engine that supports C++ servlets, basing it on the Java servlet API and Java Servlet 2.3 specification. Built on top of the Servlet Engine is a SOAP Handler (SOAPServlet), implemented as a C++ servlet, which acts as a SOAP message gateway between the Servlet Engine and SOAP envelope parsing layer.

The Servlet Engine directs all HTTP posts, which have the subpath cpp_servlet/soap/SOAPServlet in the URL to the SOAPServlet. The SOAPServlet extracts the SOAP Envelope from the HTTP message body. It then takes care of SOAP Envelope parsing and invokes the implementation specified in the SOAP request using gSOAP (http://www.cs.fsu.edu/~engelen/soap.html), an open-source SOAP stack implementation for C/C++.

Although C++WSP works on Win32 for the Apache web server, it can easily be ported to different platforms (say, UNIX) or web servers (such as Iplanet or IIS). It is approximately 6000 lines of code in a total of 67 files with about 32 classes. The source code for C++WSP is available electronically; see "Resource Center," page 5.

To develop SOAP applications for C++WSP, you first need to put the interfaces (functions) to be exposed as a SOAP web service into a C/C++ header file; see Example 1. Next, you use the soapcpp2 compiler (part of gSOAP) on the header file to generate both code and a WSDL file. The soapcpp2- generated code is compiled along with the SOAP application. The generated WSDL file describes the services offered in the SOAP application.

You then need to provide the implementation for the interface in a C++ source file; see Example 2. The implementation also calls the REGISTER_SOAP_RESOURCE macro to register the SOAP application to the SOAP Handler Factory. The implementation source files, the core gSOAP files (stdsoap2.cpp and stdsoap2.h), and the soapcpp2-generated files (soapStub.h, soapH.h, soapC.cpp, and soapServer.cpp) should be compiled together to make a shared library (DLL on Windows).

To deploy SOAP applications, you put the library in a directory relative to the web server. This implementation requires it be placed under the cpp_servlet\soap\apps directory in the Apache root directory. You also need to update cpp_servlet\soap\web.xml (Listing One) for this application. The context-param element has the SOAP resource ID (the one we use in the register macro) and library name. The init-param element has the location of the SOAP application libraries. The init-param element is common for all the applications. For each new SOAP application, you need to add a context-param element.

The soapcpp2 compiler also generates code to test the SOAP application. You do need to write a program (Example 3), however, and compile it with the generated code to get the client test executable.

Servlet Engine Design

The Servlet Engine is an extension (module) of the Apache web server. Almost all of the popular web servers provide mechanisms to extend server functionality via web-server extensions. Apache web server also provides such a facility through the Apache API. The server can be configured such that the URLs containing a particular string (ID for the extension) are routed to the server extension. The extension handler subsequently handles these requests. The Servlet Engine for Apache has an ApacheHttpServletResponse class (available electronically), which implements the generic HttpServletResponse abstract class (interface).

The Servlet Engine layer (see Figure 3) has two main components:

  • The Core Servlet Engine provides the run-time components for the layer.
  • The C++ Servlet API (CSAPI) provides the corresponding build-time components.

Figure 4 shows the main components of the Core Servlet Engine. The ServletContainer class extracts the application and servlet name from the URL. For example, in the request URL cpp_servlet/ServletApp/FileReaderServlet, ServletApp is the application name, and FileReaderServlet the servlet name. ServletContainer then gets the Servlet object from the ServletFactory using the servlet name:

HttpServlet *servletObj =

ServletFactory::getServlet("FileReaderServlet");

The container initializes the servlet object with the configuration and context parameters, from the servlet application deployment descriptor (web.xml). It then invokes the service method of the servlet. The servlet invoked can also start user sessions. The SessionManager class maintains the life of user sessions.

The ServletFactory creates the servlets for the servlet container. All the servlets get registered with the servlet factory when the server starts up and loads servlet applications deployed as a shared library. A macro REGISTER_SERVLET (ServletName, ''ServletName'') lets servlets register with the servlet factory against the servlet name. ServletFactory.h (available electronically) has the macro definition.

SOAP Handler Design

To provide a layer for C++ SOAP applications on the Servlet Engine, we integrated the Servlet Engine with gSOAP. The default run-time model for gSOAP requires that you create a CGI program for each SOAP application. gSOAP has default implementations of HTTP parsing and reading data from the standard input stream. The invocation of these functions is through function pointers. The default behavior of gSOAP can be overridden by writing new functions and assigning them to these function pointers. Thus, gSOAP (although written in C) provides good polymorphism mechanisms by means of function pointers. Figure 5 shows the components of the SOAP Handler.

We wrote the gSOAP Interface layer to make gSOAP work with SOAPServlet for receiving the SOAP messages, and with ServletOutputStream (a class available inside servlets' service methods for writing back data to the client) for sending back the SOAP responses. The overridden gSOAP functions send, receive, response, and parse are kept in the structure called "soap" (Listing Two). We overrode the default implementations by assigning our implementations to these function pointers. We also use the user variable (of type void*) in the soap structure to pass data (HTTPServletRequest and HttpServletResponse objects) to the functions we implemented (see gsoap_overwrite.h and gsoap_overwrite.cpp; available electronically).

Every SOAP application must have one unique identifier (soap resource name). The SOAPHandlerFactory class is used by all the SOAP applications deployed on the platform to register their sets of gSOAP function pointers against the SOAP resource name. This is done via a macro such as REGISTER_SOAP_RESOURCE("trysoap", trysoap) that works this way: When the application (in the form of a shared library) gets loaded into memory, the structure SOAPHandlerStruct (Example 4) with all the function pointers for the app is registered to the SOAPHandlerFactory against the application identifier. Listing Three is the macro definition, and Table 2 describes the gSOAP functions.

To illustrate a SOAP request posted on to the C++WSP, assume the URL of the posted request contains the string cpp_servlet/soap/SOAPServlet?soap_res=someSoapAppName. While parsing this request, the web server determines that the request is for the Servlet Engine, since the URL contains cpp_servlet. The Servlet Engine extracts the servlet application name ("soap") and servlet name ("SOAPServlet") from the URL. The Servlet Engine then creates the SOAPServlet and hands over the HTTP request containing the SOAP envelope to it by invoking the servlet's service method, which results in SOAPServlet's doGet method getting called.

Listing Four includes the doGet method of the servlet. In this method, SOAPServlet determines the SOAP resource name for the soap_res parameter. It then uses the SOAPHandlerFactory to get the handle to the appropriate SOAP application through the registered instance of SOAPHandlerStruct. It then calls the gSOAP soap_init(), soap_serve(), and soap_end() functions of the SOAP application using the function pointers of the SOAPHandlerStruct.

Conclusion

C++WSP is useful for quickly exposing existing C/C++ systems as web services. Our next step is to utilize the platform independence of the current design for porting it to different web servers and different platforms. We also need to do some fine-tuning on this platform and performance benchmarking of it against Java servlet containers.

Acknowledgments

Thanks to N.S. Nagaraj and Srinivas Thonse for the guidance and encouragement they have provided us in this work. Also, thanks to Srinivas Thonse, Abdul Sakib Mondal, and Shubhashis Sengupta for useful comments on this article.

DDJ

Listing One

// Web.xml for SOAP application configuration
    <!-- Context parameters for the web application as key value pairs -- >
    <!-- Information about the SOAP applications deployed -- >
    <context-param>
        <param-name> quote </param-name>
        <param-value> quoteEx.dll </param-value>
    </context-param>
        <context-param>
        <param-name> trysoap </param-name>
        <param-value> trysoap.dll </param-value>
    </context-param>
    <!-- Information about the deployed servlets -->
    <servlet>
       <servlet-name> SOAPServlet </servlet-name>
        <description>
            Handler of SOAP requests. SOAP servlet Application as viewed by 
            servlet container and SOAP handler as viewed by SOAP applications.
        </description>
              <!-Servlet initialization parameters as key value pairs -- >
        <init-param>
            <param-name> soap_app_location </param-name>
            <param-value> 
                C:\Program Files\C++WSP\Apache2\cpp_servlet\soap\apps\
            </param-value>
        </init-param>     
    </servlet>

Back to Article

Listing Two

// Shows gSOAP structure soap containing the function pointers to default 
// implementations in fsend, fparse, frecv, fresponse variables. Also shows 
// user variable of type void *, which we use to pass HttpServletRequest 
// and HttpServletResponse objects
struct soap
{ 
  /*** Some data types deleted (not shown in the listing) ***/
  void *user;        /* reserved for callbacks to pass user-defined data */
  int (*fpost)(struct soap*, const char*, const char*, const char*, 
                                                       const char*, size_t);
  int (*fposthdr)(struct soap*, const char*, const char*);
  int (*fresponse)(struct soap*, int, size_t);
  int (*fparse)(struct soap*);
  int (*fparsehdr)(struct soap*, const char*, const char*);
  int (*fopen)(struct soap*, const char*, const char*, int);
  int (*fclose)(struct soap*);
  int (*fsend)(struct soap*, const char*, size_t);
  size_t (*frecv)(struct soap*, char*, size_t);
  int (*fignore)(struct soap*, const char*);
};

Back to Article

Listing Three

// definition of REGISTER_SOAP_RESOURCE macro. This macro defines a dummy 
// struct after forming a unique struct name. If SOAP_RESOURCE_NAME is Quote, 
// then struct name is dummySoapQuote. struct's constructor first sets 
// application's soap_init, soap_serve and soap_end functions' addresses to
// SOAPHandlerStruct member function pointers. Then it registers SOAP 
// Application's name (key) and application's SOAPHandlerStruct 
// instance(value) to SOAPHandlerFactory by calling registerToFactory method.
// Then invoke constructor by having a dummy instance of defined struct. For 
// instance, if app name is Quote, it evolves to dummySoapQuote 
// dummySoapInstanceQuote; When shared library of application gets loaded, 
// dummySoapInstanceQuote is instantiated. This triggers SOAP application 
// registering to the factory

#define REGISTER_SOAP_RESOURCE(SOAP_RESOURCE_NAME_STR, SOAP_RESOURCE_NAME) \
    SOAPHandlerStruct SOAPHandlerStruct##SOAP_RESOURCE_NAME##Instance; \
\
struct dummySoap##SOAP_RESOURCE_NAME { \
    dummySoap##SOAP_RESOURCE_NAME() \
{\
    SOAPHandlerStruct##SOAP_RESOURCE_NAME##Instance.soap_init_fn_ptr = 
                                                               soap_init; \
    SOAPHandlerStruct##SOAP_RESOURCE_NAME##Instance.soap_serve_fn_ptr = 
                                                               soap_serve; \
    SOAPHandlerStruct##SOAP_RESOURCE_NAME##Instance.soap_end_fn_ptr = 
                                                               soap_end; \
    SOAPHandlerFactory::registerToFactory( SOAP_RESOURCE_NAME_STR, 
                       &SOAPHandlerStruct##SOAP_RESOURCE_NAME##Instance ); \
}\
};\
    struct dummySoap##SOAP_RESOURCE_NAME 
                                    dummySoapInstance##SOAP_RESOURCE_NAME##;

Back to Article

Listing Four

// doGet method of the SOAPServlet.
void SOAPServlet::doGet(HttpServletRequest & req, HttpServletResponse & resp)
    throw( ServletException)
{   
    // Get the output stream object reference
    ServletOutputStream & srvltOpStream = resp.getOutputStream();
    // Get the SOAP resource id value of SOAP_RESOURCE_KEY is "soapres"
    std::string soapRes = req.getParameter(SOAP_RESOURCE_KEY);
    std::string soapResDll;
    bool soapResFound = false;
    // Initialize SOAPHandlerFactory, if not already initialized. Loads all 
    // deployed SOAP applications. Applications on loading, registers to 
    // the SOAPHandlerFactory.
    if(!SOAPHandlerFactory::Initialized())
    {
        initialize();
    }
    // Get the application specific instance of SOAPHandlerStruct
    // which has the function pointers to application's 
    // soap_init(), soap_serve() and soap_end() functions.
struct SOAPHandlerStruct * hdlrStruct = 
                SOAPHandlerFactory::getSOAPHandlerStruct(soapRes.c_str() );
    if(hdlrStruct == NULL)
    {
        // return error  
    resp.sendError(HttpServletResponse::SC_NOT_IMPLEMENTED); 
                                                      // 501 not implemented
        return;
    }
    resp.setContentType("text/xml");
    struct soap psoap;
    gsoapUserData pRqConf;
    pRqConf._srvltReq = &req;
    pRqConf._srvltResp = &resp;
    // Initialize the soap structure
    (*hdlrStruct->soap_init_fn_ptr) (&psoap);
    psoap.user = &pRqConf;
    set_callbacks(&pRqConf, &psoap);
    // Serve the SOAP request
    int nRet = (*hdlrStruct->soap_serve_fn_ptr)(&psoap);
    // cleanup the soap structure
    (*hdlrStruct->soap_end_fn_ptr)(&psoap);
    // Write output back to the client
    resp.flushBuffer();
}



Back to Article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video