SOAP Agent  

A C++ SOAP Client Library v3.0

 

1.    Introduction

SoapAgent is a C++ library for accessing web services. With the library, users can access web services using just a few lines of code with no SOAP or XML knowledge required.  The library is an independent implementation of SOAP 1.1 specification. It supports Web Service Definition Language (WSDL).

 

The library was built using Microsoft VC++ 6.0 as a multi-threaded DLL.                 

 

1.1.            Key Features

·         Support WSDL.

·         Flexible SOAP Message Template (See below).

·         Does not depend on third party components.

·         Embedded transport protocols.

·         Plug-in SAX XML Parser.

·         Light weight, with very small memory footprint.

·         Works on Win 95/98/NT/2000.

·         Compatible with many SOAP 1.1 implementations.

·         Capable of handling any data types (with message templates).

 

1.2.            Limitations

The version 3.0 of the library has the following limitations:

·         Works only on Windows platform.

·         Support only UTF-8 encoding.

2.    Installation

The installation is very easy. After downloading the library, run the installation program. It will copy the following files into the specified directory:

 

Debug:                   A directory contains the debug version of SoapActor.dll

Sample:                  A directory contains a sample SOAP client function.

SoapActor.dll:      The SoapAgent implementation library.

SoapActor.lib:      The exported library.

SoapClient.doc:    The programming guide     

SoapClient.lic:       A generated license file. It is required for using SoapAgent.dll

SoapClientApi.h:  The main header file.

SoapTest.exe:       A small test program.

XMLParser.dll:     The XML Parser implementation.

dbsock.dll:             A SOAP transport DLL.

license.txt:             The license agreement.

readme.txt:             Last minutes changes and other information.

zlib.dll:                    A data compression library.

 

The SAMPLE subdirectory contains a sample project that demonstrates how to use the library.

3.    Using the library

3.1.            Environment Settings

·         Include the SoapClientApi..h file in your project, remember to add the SoapAgent directory in the INCLUDE path.

·         Make sure your linker can locate SoapActor.lib by adding it to your LIB path.

 

3.2.            SoapAgent definition

All SOAP client services are provided inside a SoapAgent class. Its definition is in the SoapClientApi.h file. The file defines a pure virtual class and two exported functions from the class factory:

 

class SOAPAPI SoapAgent

{

       public:

              // execute a SOAP method given an WSDL file

              virtual HRESULT ExecuteMethod(const TCHAR* szWSDL, const TCHAR*  szMethodName, TCHAR* * ppInputArray, std::basic_string<TCHAR> **ppOutputArray, unsigned int*pOutSize)=0;

              // execute a SOAP method given a message template

              virtual HRESULT ExecuteMethod(const TCHAR* szServerURI, const TCHAR* szTemplateFile, const TCHAR* szMethodName, TCHAR**ppNameArray, TCHAR** ppInputArray, std::basic_string<TCHAR> **ppOutputArray, unsigned int*pOutSize)=0;

              // set the proxy

              virtual void SetProxy(const TCHAR* szProxyServer, short nPort=80, const TCHAR* szProxyUser=NULL, const TCHAR* bstrProxyPwd=NULL)=0;

              // get error description

              virtual TCHAR *GetErrorString()=0;

              // set the request header

              virtual void SetHeader(const TCHAR* szName, const TCHAR * szValue)=0;

              // clear the request and response objects

              virtual void Clear()=0;

              // get the complete response string or a particular element

              virtual TCHAR* GetResponse(const TCHAR* szElementName, bool bUseNamespace)=0;

 

};

 

extern "C" SoapAgent* MakeSoapAgent(const TCHAR* szUserName, const TCHAR* szPassword, int nDebugMode=0);

extern "C" void DestroySoapAgent(SoapAgent* );

 

The SoapAgent specification is implemented inside the library, as the matter of fact, there are several implementations of the class. The class library creates a type of SoapAgent object based on the current configuration and the nature of the invocation.

 

There is no constructor and destructors in SoapAgent definition, so it cannot be constructed using the new operator. Instead, the library offers two exported functions: MakeSoapAgent and DestroySoapAgent,  for object management.

 

3.3.            Using SoapAgent Objects

 

Here are some simple steps for using the SoapAgent objects:

1.    Create a SoapAgnet Object:
     SoapAgent * pMyAgent= MakeSoapAgent(NULL, NULL);

2.        Define an array of parameters to be passed to the remote server.

3.        Call the ExecuteMethod function and specify the WSDL and name of the method to be executed.

4.        Process the returned results.

5.        Destroy the SoapAgent using the DestroySoapAgent function.

 

This is best illustrated by the sample program below.

3.3.1.                  A Sample Program

The sample shows how to write a SOAP client using the  SoapAgent object. The function invokes the Method1 function provided by a test web service at http://www.SoapClient.com, which  echoes the two input string parameters.

 

SoapResponder()

{

       // initialize the parameter array for the method call. The array must end with NULL.

       TCHAR* pParams[3]={"My First Param", "My Second Param", NULL};

       SoapAgent *pSoapAgent= MakeSoapAgent(NULL, NULL);

       std::string * pOutput;

       unsigned int nSize=0;

       HRESULT hr;

       if(SUCCEEDED(hr=pSoapAgent->ExecuteMethod(

              "http://www.soapclient.com/xml/soapresponder.wsdl", // WSDL file

              "Method1",           // method name to be invoked.

              pParams,             // array of input parameters.

              &pOutput,            // array of output parameters

              &nSize               // number of output variable in pOutput

              )))

       {

              // print out results

              for(int i=0; i<nSize; i++)

                     _tprintf("%s\n", pOutput[i].c_str());

       }

       else

       {

              // obtain error string when failed.

              _tprintf("Error String %s\n", pSoapAgent->GetErrorString());

       }

       DestroySoapAgent(pSoapAgent);

       return 0;

}

3.4.            Passing Parameters

3.4.1.                  Simple Data Types

Parameters of simple data types are passed as strings to the ExecuteMethod function. They are bound to the proper variables in the request message using the WSDL file.

 

For example, to invoke a SOAP function with 5 parameters of different types as follows:

 

  <param1 xsi:type="xsd:int">5643</param1>
  <param2 xsi:type="xsd:boolean">false</param2>
  <param3 xsi:type="xsd:string">Delaware</param3>
  <param4 xsi:type="xsd:double">9683.66666667</param4>
  <param5 xsi:type="xsd:timeInstant">1904-01-01T04:47:47-08:00</param5>
 
You may use an input parameter array:

                TCHAR* ppParams[6]={“5643”,”false”,”Delaware”,

 “9683.667”,” 1904-01-01T04:47:47-08:00”, NULL};

 

Note that the last element in the array is NULL, this is the only way SoapAgent object can figure out the size of the array.  The SoapAgent object constructs a request message similar to the following:

 

<?xml version="1.0"?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/1999/XMLSchema" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance">
  <SOAP-ENV:Body>
         <myRequestMessage>
                 <param1 xsi:type="xsd:int">5643</param1>
                 <param2 xsi:type="xsd:boolean">false</param2>
                 <param3 xsi:type="xsd:string">Delaware</param3>
                 <param4 xsi:type="xsd:double">9683.66666667</param4>
                 <param5 xsi:type="xsd:timeInstant">1904-01-01T04:47:47-08:00</param5>
                 </myRequestMessage>
         </SOAP-ENV:Body>
  </SOAP-ENV:Envelope>

 

As can be seen from the example, the order of your parameter is very important. It must match that defined in the WSDL file.

 

3.4.2.                  Array Data Types

The library supports single-dimension array parameters. Array parameters are passed in the similar fashion as the simple data types. Suppose there is a function with the following parameters:

 

1.        A string variable.

2.        A Boolean variable.

3.        An array of three integers.

 

You may use an input parameter array as the following:

 

                TCHAR* ppParams[6]={“Delaware”,”false”,”-23”,

 “34”,”-16”, NULL};

 

The parameters are put into the request message in sequential order:

 

  <myRequestMessage>
         <param1 xsi:type="xsd:int">Delaware</param1>
         <param2 xsi:type="xsd:boolean">false</param2>
         <param3 SOAP-ENC:arrayType="xsd:int[3]">
                 <item>-23</item>
                 <item>34</item>
                 <item>-16</item>
         </param3>
  </myRequestMessage>
 

The assumption here is that the number of elements in the array is fixed.

 

3.5.            Message Templates

A message template is a file that contains a SOAP message with special tokens in it. The tokens are substituted at run-time using input values. Tokens are enclosed by “[$” and “]” in the file. For example, the following is a message template for a SOAP function with a single string parameter:

 

<?xml version="1.0"?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/1999/XMLSchema" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance">
  <SOAP-ENV:Body>
         <[$MethodName]Request>
                 <param3 xsi:type="xsd:string">[$param3]</param3>
         </<[$MethodName]Request>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

 

[$MethodName] and [$param3] are token variables in the message template. They are replaced by values of MethodName and param3 when the template is instantiated.

 

3.5.1.                  Template Applications

There are many situations where template messages can be used, and they prove to be very powerful:

 

1.        WSDL Unavailable:  When there is no service description file available to aid parameter bindings, it is very difficult to construct SOAP request dynamically. Many SDKs or Toolkits simply don’t work without a WSDL file.  A message template file makes the job much easier.

2.        Complex Data Types: There are situations where many user-defined types involved in a SOAP method.  A template message can greatly simplify messages construction and parameter binding.

3.     When a web service offers optional parameters or variable number of parameters, message template provides an easy solution.

 

 

There are many advantages in using template messages:

1.        Build Generic Applications: Template messages allow you to take out service specific elements from your applications. So that a client application can be used to access many web services by simply using different template message files. SoapClient.com web site uses this technique to access user-defined web services.

2.        Handle Compatibility Issues: It is unfortunate that SOAP servers sometimes don’t work with each other. Even they are all SOAP 1.1 compliant in the future; there will still be differences of schemas and encoding styles for the same web service. A separate message template can be created for each service to deal with such problems.

3.        Universal Data Handling: With template file, any structure that can be expressed in XML format could be put into a pre-constructed file. There is really no data type that can’t be handled theoretically. Data binding becomes simple string substitution.

4.        Lower Maintenance Cost: Most of the SOAP SDK and Toolkits do static service binding by creating objects that match to the service definition file. Problems occur when the function signature changes, which is more likely than the traditional programming model because web services are often provided by third parties. The client side object would have to be recompiled, linked and tested. With message templates, it is as simple as editing a text file.

 

3.5.2.                  Invoke Method with Template

There are two ExecuteMethod functions in the SoapAgent class: one uses WSDL files and another uses message templates. The latter is defined as follows:

 

virtual HRESULT ExecuteMethod(const TCHAR* szServerURI, const TCHAR* szTemplateFile, const TCHAR* szMethodName, TCHAR**ppNameArray, TCHAR** ppInputArray, std::basic_string<TCHAR> **ppOutputArray, unsigned int*pOutSize);

It requires the following parameters:

·         szServerURI: The absolute URI for the web service, sometime referred to as the endpoint.

·         szTemplateFile: The name of the message template file.

·         ppNameArray: An array of names of the input variables.

·         ppInputArray: An array of string values of the input variables.

·         ppOutputArray: An array of output variables in the SOAP response message.

·         pOutSize: The number of elements in the ppOutputArray.

 

The function takes care of all the complicated tasks such as message construction, wiring and response handling. The template file is instantiated using the supplied name-value array.

 

3.6.            Processing Responses

When WSDL is given, the SoapAgent object does good job parsing SOAP response messages and put returned variables into the ppValues array. There are situations, however, where the responses are so complex that a custom processing is needed. This can be done using the GetResponse funtion.

 

When a  SOAP responses is received, it is loaded into a XML parser and decomposed into a tree-like structure. The GetResponse function allows direct access to all nodes in the tree structure. A node in the tree is addressed using all node names, separated by the '|' character. Let's assume that the following message is received:

 

<?xml version='1.0' encoding='UTF-8'?>

 

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org/1999/XMLSchema">

<SOAP-ENV:Body>

<ns1:getTempResponse xmlns:ns1="urn:xmethods-Temperature" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">

<return xsi:type="xsd:float">41.0</return>

</ns1:getTempResponse>

</SOAP-ENV:Body>

</SOAP-ENV:Envelope>

 

GetResponse("Envelope", false) would returns a string that contains:

 

<SOAP-ENV:Body>

<ns1:getTempResponse xmlns:ns1="urn:xmethods-Temperature" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">

<return xsi:type="xsd:float">41.0</return>

</ns1:getTempResponse>

</SOAP-ENV:Body>

 

 and GetResponse("Envelope|Body|getTempResponse", false) gives:

<return xsi:type="xsd:float">41.0</return>

Finally, GetResponse("Envelope|Body|getTempResponse|return", false) returns just 41.0.

 

If the second parameter is true, element names must include the name space prefix.

 

 

3.7.            Using SSL

The library has built-in support for SSL. It switches to SSL automatically when the endpoint uses HTTPS.  Protocol switches under the following situations:

·         Executing Method with WSDL: SSL will be used if the soap:address element in the service definition specified HTTPS in the URL.

·         Executing Method with template: SSL will be used if the HTTPS is specified in the szServerURL parameter.

4.    Reference

 

4.1.            MakeSoapAgent

extern "C" SoapAgent* MakeSoapAgent(const TCHAR* szUserName, const TCHAR* szPassword, int nDebugMode=0);

 

4.1.1.                  Input Parameters

·         szUserName : The name of the user if authentication is required, NULL otherwise.

·         szPassword: The password string for accessing a web service. It can be NULL if authentication is not required.

·         nDebugMode: This is an integer value from 0 to 5 that specifies the logging mode, with 5 being the most verbose. The library creates a log file, named debug.log, under the current directory if nDebugMode is none zero, and puts detailed processing information into the file. nDebugMode=4 is adequate in most of the situations.

4.1.2.                  Return

The function returns a SoapAgent object.

4.1.3.                  Description

The class factory creates a SoapAgent object when the function is called. The class factory may caches the object for later use when needed.

4.2.            DestroySoapAgent

extern "C" void DestroySoapAgent(SoapAgent* pSoapAgent);

 

4.2.1.                  Input Parameters

·         pSoapAgent : A pointer the SoapAgent  object.

 

4.2.2.                  Return

None.

4.2.3.                  Description

The function should always be called in pair with MakeSoapAgnet. It releases a SoapAgent object. The object may not necessarily be deleted from memory if the class factory is cache enabled.

4.3.            ExecuteMethod

virtual HRESULT ExecuteMethod(const TCHAR* szWSDL, const TCHAR*  szMethodName, TCHAR* * ppInputArray, std::basic_string<TCHAR> **ppOutputArray, unsigned int*pOutSize);

4.3.1.                  Input Parameters

·         szWSDL : The address of the web service description file. It can be a local file or a remote file given by an absolute URI.

·         szMethodName: The name of the method to be invoked.

·         ppInputArray: An array of string pointers to be passed as input parameters. The last element in the array must be NULL. All parameters are passed as strings, although the library may perform conversion when needed.

·         ppOutputArray:  An array of string pointers that are returned from the server, including the returned value and those parameters passed by references. Note that the library owns the memory allocated for the returned values, the caller should not free the returned pointers.

·         pOutSize: A pointer that points to the total number of  returned parameters in ppOutputArray.

 

4.3.2.                  Return

The function returns S_OK if the method call was successful, which only means that it successfully received the response from the server. The method itself may have failed, however, on the server side due to other reasons. The GetErrorString may be used for examining the causes of a failure.

 

4.3.3.                  Description

This is the main function to access remote methods using the library.  Note that the ppInputArray may be empty if no input parameter is required. There may also be no returned value from SOAP server even under normal situation (i.e., a one-way message).

4.4.            ExecuteMethod

HRESULT ExecuteMethod(const TCHAR* szServerURI, const TCHAR* szTemplateFile, const TCHAR* szMethodName, TCHAR**ppNameArray, TCHAR** ppInputArray, std::basic_string<TCHAR> **ppOutputArray, unsigned int*pOutSize);

 

4.4.1.                  Input Parameters

·         szServerURL : The URL where the service can be accessed. It is also called the end point.

·         szTemplateFile: The name of the message template file.

·         szMethodName: The name of the remote method.

·         ppNameArray: This is an array of variable names to be used in the template instantiation. The last element of the array must be NULL.

·         ppInputArray: An array of string pointers to be used as values in the message template. The last element in the array must be NULL.

·         ppOutputArray:  An array of string pointers that are returned from the server, including the returned value and those parameters passed by references. Note that the library owns the memory allocated for the returned values, the caller should not free the returned pointers.

·         pOutSize: A pointer that points to the total number of  returned parameters in ppOutputArray.

 

4.4.2.                  Return

The function returns S_OK if the method call was successful, which only means that it successfully received the response from the server. The method itself may have failed, however, on the server side due to other reasons. The GetErrorString may be used for examining the causes of a failure.

 

4.4.3.                  Description

This function accesses the remote method using a template message.  Template tokens in the message template are substituted by the values given in the ppInputArray. Note that the ppInputArray may be empty if no input parameter is required. There may also be no returned value from SOAP server even under normal situation (i.e., a one-way message).

4.5.            SetProxy

void SetProxy(const TCHAR* szProxyServer, short nPort=80, const TCHAR* szProxyUser=NULL, const TCHAR* bstrProxyPwd=NULL);

4.5.1.                  Input Parameters

·         szProxyServer: The name of the proxy server.

·         nPort: The port number of the proxy server.

·         szProxyUser: The authorized user name for the proxy server if authentication is required.

·         bstrProxyPwd:  The password.

 

4.5.2.                  Return

None

 

4.5.3.                  Description

Call this function only when there is a proxy server between the SOAP client and the SOAP server. The function must be called before ExecuteMethod for the proxy to take effect. The function adds the Authorization header in the message if user name and password are provided.

4.6.            GetErrorString

TCHAR *GetErrorString();

4.6.1.                  Input Parameters

·         None

 

4.6.2.                  Return

The string contains error messages.

 

4.6.3.                  Description

The function returns error occurred on both the client side and/or server side.  These may include:

1.        Any error conditions that happened before posting data to the server.

2.        Error condition occurred during data transmission.

3.        Error returned by the remote function.

4.        Fault conditions defined by the SOAP protocol.

 

4.7.            GetResponse

TCHAR* GetResponse(const TCHAR* szElementName, bool bUseNamespace)=0;

4.7.1.                  Input Parameters

·         szElementName: The name of the element.  It can contain names of multiple levels separated by '|'. For example, "Envelope|Body" will return the body part of the response. The complete response message will be returned if the parameter is NULL.

·         bUseNamespace: The parameter should be true if szElementName contains name space prefix. Otherwise false.

 

4.7.2.                  Return

The string represents the object identified by szElementName. It may be an XML structure if the object is not a leaf node.

 

4.7.3.                  Description

The function is usually used to deal with complicated structures from a SOAP response. The ExecuteMethod function puts all simple values into the ppValues array in most cases.

 

4.7.4.                  Example

The following code gets a SOAP response body:

GetResponse("Envelope|Body", false);