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

C++WSP quickly exposes existing C/C++ systems as web services, and it's more elegant than JNI while avoiding the platform limitations of .NET.


April 01, 2003
URL:http://www.drdobbs.com/cwsp-a-c-web-services-platform/184405313

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 [email protected] 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:

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:

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:

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

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


// Sample SOAP interface.
typedef struct ResponseStruct
{
    int status;
    char * message;
} Response;
// struct ResponseStruct & rS
int ns__trySoap(char * name, Response * rS);

Example 1: Sample SOAP interface.

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


// Implementation of SOAP Interface. This macro is required to be 
// called to register the functions soap_init(), soap_serve(), and 
// soap_end() to the SOAPHandlerFactory
REGISTER_SOAP_RESOURCE("trysoap", trysoap)
int ns__trySoap(struct soap * s, char * name, Response * rS)
{
    rS->status = 1;
    char tempStr[250];
    strcpy(tempStr, "\"Hello ");
    if(name == NULL || strlen(name) > 100)
        return SOAP_CLI_FAULT;
    strcat(tempStr,  name);
    strcat (tempStr, "\" says ns__trySoap");
    rS->message = strdup(tempStr);
    return SOAP_OK;
}

Example 2: Implementation of the SOAP interface.

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


// soap application test client main program
  soap_init(&soap); // must initialize
  Response rs;
  if (soap_call_ns__trySoap(&soap, queryStr, NULL, name, &rs) == 0)
    printf("\nStatus - %d    Message - %s\n", rs.status, rs.message);
  else
  { soap_print_fault(&soap, stderr);
    soap_print_fault_location(&soap, stderr);
  }
  return 0;

Example 3: SOAP application test client (main program).

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


typedef  void (*soap_init_fn)(struct soap *); 
typedef  int (*soap_serve_fn)(struct soap *);  
typedef  void (*soap_end_fn)(struct soap*); 
struct SOAPHandlerStruct
{
    soap_init_fn soap_init_fn_ptr;
    soap_serve_fn soap_serve_fn_ptr;
    soap_end_fn soap_end_fn_ptr;
};

Example 4: SOAPHandlerStruct definition.

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

POST /StockQuote HTTP/1.1
Host: www.stockquoteserver.com
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn
SOAPAction: "Some-URI"
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
        SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <SOAP-ENV:Body>
        <m:GetLastTradePrice xmlns:m="Some-URI">
                <symbol>DIS</symbol>
            </m:GetLastTradePrice>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Figure 1: Sample SOAP request enveloped in an HTTP request.

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

HTTP/1.1 200 OK
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn

<SOAP-ENV:Envelopexmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
        SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
    <SOAP-ENV:Body>
        <m:GetLastTradePriceResponse xmlns:m="Some-URI">
            <Price>34.5</Price>
        </m:GetLastTradePriceResponse>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Figure 2: Sample SOAP response enveloped in an HTTP response.

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

Figure 3: C++WSP architecture.

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

Figure 4: Core Servlet Engine Components.

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

Figure 5: SOAP Handler Components.

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.