In the MFC/C++ environment, there are several libraries/tools can be used to integrate with Java Web Service. They are ATL Server, Windows Web Service API and gSoap.
1, ATL Server
ATL Server is C++ Template based technology in Vistual Studio environment.It provides an utility tool to generate proxy code for Web Service client. From Visual Studio 2008, Mirosoft open sourced it, and its official Web site is http://atlserver.codeplex.com. We can download the ATL_Server_Source_and_Headers_9_0_70425_Alpha.zip. After unzipping this file, we can find a solution file under the source sub folder for this tool. It is in the VS 2005 version, which need converted into VS 2008 version once we double click it to open it. Open the solution file, add the ATL Server include folder in the property
Then build this sproxy.sln , we can get the sproxy.exe utility.
In Microsoft MSDN, We can find some useful link about how to use the sproxy.exe.
sproxy [ options ] [ /out:outputfile ] input_location
The input_location can be a local WSDL file or HTTP link for a WSDL.
For example:
sproxy /wsdl http://webservice.webxml.com.cn/WebServices/WeatherWS.asmx?WSDL
will generate a header file WeatherWS.h.We can add this file in our Web Service Client project to access like:
#include "WeatherWS.h"
using namespace WeatherWS;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
CoInitialize(NULL);
HRESULT hr = S_OK;
CWeatherWST<CSoapSocketClientT<>> * m_srv = new CWeatherWST<CSoapSocketClientT<>>;
CComBSTR cityCode = "";
CComBSTR userId = "";
CComBSTR * bstrOut;
int size;
hr = m_srv->getWeather(cityCode, userId, (BSTR * *)&bstrOut, &size);
if(FAILED(hr))
{
}
else
{
std::cout << bstrOut->m_str << std::endl;
}
if (m_srv != NULL)
delete m_srv;
CoUninitialize();
return 0;
}
This snippet of code can run successfully.
But when we generate some other Web Service WSDL file, the generated file will cause runtime exception. We created the Web Service – User Detail to get available application for specific user. The WSDL content for this Web Service is listed following:
<?xml version="1.0" encoding="UTF-8" ?>
- <wsdl:definitions name="UserDetailServiceImplService" targetNamespace="http://security.ipns.enterprise.argushealth.com/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://security.ipns.enterprise.argushealth.com/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
- <wsdl:types>
- <xs:schema elementFormDefault="unqualified" targetNamespace="http://security.ipns.enterprise.argushealth.com/" version="1.0" xmlns:tns="http://security.ipns.enterprise.argushealth.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="getAvailableApplications" type="tns:getAvailableApplications" />
<xs:element name="getAvailableApplicationsResponse" type="tns:getAvailableApplicationsResponse" />
- <xs:complexType name="getAvailableApplications">
- <xs:sequence>
<xs:element minOccurs="0" name="arg0" type="xs:string" />
</xs:sequence>
</xs:complexType>
- <xs:complexType name="getAvailableApplicationsResponse">
- <xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" name="return" type="tns:application" />
</xs:sequence>
</xs:complexType>
- <xs:complexType name="application">
- <xs:sequence>
<xs:element minOccurs="0" name="name" type="xs:string" />
<xs:element minOccurs="0" name="url" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>
</wsdl:types>
- <wsdl:message name="getAvailableApplications">
<wsdl:part element="tns:getAvailableApplications" name="parameters" />
</wsdl:message>
- <wsdl:message name="getAvailableApplicationsResponse">
<wsdl:part element="tns:getAvailableApplicationsResponse" name="parameters" />
</wsdl:message>
- <wsdl:portType name="UserDetailService">
- <wsdl:operation name="getAvailableApplications">
<wsdl:input message="tns:getAvailableApplications" name="getAvailableApplications" />
<wsdl:output message="tns:getAvailableApplicationsResponse" name="getAvailableApplicationsResponse" />
</wsdl:operation>
</wsdl:portType>
- <wsdl:binding name="UserDetailServiceImplServiceSoapBinding" type="tns:UserDetailService">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
- <wsdl:operation name="getAvailableApplications">
<soap:operation soapAction="" style="document" />
- <wsdl:input name="getAvailableApplications">
<soap:body use="literal" />
</wsdl:input>
- <wsdl:output name="getAvailableApplicationsResponse">
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
- <wsdl:service name="UserDetailServiceImplService">
- <wsdl:port binding="tns:UserDetailServiceImplServiceSoapBinding" name="UserDetailServiceImplPort">
<soap:address location="http://172.16.23.25:9081/user-application-ws/UserDetail" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
After running
sproxy /wsdl http://172.16.23.25:9081/user-application-ws/UserDetail?wsdl
We add the generated file UserDetailServiceImplService.h to RSOClient project, use the following snippet of code to access the Web Service:
#include "UserDetailServiceImplService.h"
using namespace UserDetailServiceImplService;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
CoInitialize(NULL);
HRESULT hr = S_OK;
CUserDetailServiceImplServiceT<CSoapSocketClientT<>> * m_srv = new CUserDetailServiceImplServiceT<CSoapSocketClientT<>>;
CComBSTR userId = "charles";
CComBSTR * bstrOut;
int size;
getAvailableApplications arg;
application AvailableApplicationsResponse;
application* p = &AvailableApplicationsResponse;
application** ppAvailableApplicationsResponse = &p;
arg.arg0 = L"charles";
hr = m_srv->_getAvailableApplications(arg, ppAvailableApplicationsResponse, &size);
if(FAILED(hr))
{
std::cout<< L"fail" << std::endl;
}
else
{
for (int i = 0; i < size; i++)
{
std::cout << ppAvailableApplicationsResponse[i]->name << std::endl;
std::cout << ppAvailableApplicationsResponse[i]->url << std::endl;
}
}
if (m_srv != NULL)
delete m_srv;
CoUninitialize();
}
hr = m_srv->_getAvailableApplications(arg, ppAvailableApplicationsResponse, &size);
Always goes to
if(FAILED(hr))
{
std::cout<< L"fail" << std::endl;
}
After carefully debugging we found that the generated proxy is sending incorrect SOAP content. From the captured the network package, we can see
There are one more <getAvailableApplications> sub element under <getAvailableApplications>. But from the WSDL file, the getAvailableApplications element should be
The element arg0 is the direct child element of getAvailableApplications. When we evaluated the gSOAP, we found that gSOAP generates the correct format.
2, Windows Web Service API
WWSAPI is a native-code implementation of SOAP which provides core network communication functionality by supporting a set of the WS-* and .NET-* family of protocols. WWSAPI is designed to be used by components/applications which fall into one of the following categories:
- Native code mandate
- Require minimal dependencies
- Require minimal startup time
- Memory constrained environments
Windows Web Services API (WWSAPI) is an operating-system component of the following operating systems:
- Windows 7
- Windows Server 2008 R2
- XP SP3
- Vista
Windows Web Services API sounds like a perfect option for us, all the original download link are not available now. It needs us send formal business email to Microsoft to get redistributable installers. We mainly referenced to this official blog of MSDN . http://blogs.msdn.com/b/windowssdk/archive/2009/10/20/building-web-services-on-windows-with-c.aspx
3, gSOAP
The official web site for gSOAP is http://sourceforge.net/projects/gsoap2. From there we can learn that:
The gSOAP toolkit is a portable C and C++ software development toolkit for XML Web services and generic XML data bindings. Easy-to-use XML auto-serialization allows you to directly integrate C and C++ data with XML. Includes WSDL/XSD schema binding and auto-coding tools, stub/skeleton compiler, Web servers (with SSL), integrated XML processing with schema validation, fast MIME/MTOM streaming, SOAP and REST, WS-* protocols (WS-Security, WS-Policy, etc), XML-RPC and JSON serialization.
Currently the stable version is 2.8.8.After download and unzip the gsoap_2.8.8.zip file, we can find there are two binary tools under the sub folder gsoap/bin/win32:
wsdl2h.exe
soapcpp2.exe
There is also source code for them under the src folder. Of course we can build both of them from scratch. There is VisualStudio2005 folder, under which the Readme is very importance. Follow the Readme, we can also build the two tools by ourselves .
The two tools has some function as sproxy.exe in ATL Server. The difference is we need do it with two steps. First we use wsdl2h to generate a header file , then use soapcpp2 to generate the files we need use in our project base on this header file. For example:
1, wsdl2h -o userservice.h -n namespacename URL or file name
the generatd userservice.h file will be used in the next step.
2, soapcpp2 userservice.h -C -i -x -I F:\study_code\mfc\gsoap_2.8.8\gsoap-2.8\gsoap\import
F:\study_code\mfc\gsoap_2.8.8\gsoap-2.8\gsoap\import is the import folder under the gsoap.
soapcpp2 will generate totally fix files:
- soaph.h
- soapc.cpp
- soapStub.h
- soapUserDetailServiceImplServiceSoapBindingProxy.cpp
- soapUserDetailServiceImplServiceSoapBindingProxy.h
- UserDetailServiceImplServiceSoapBinding.nsmap
After generating the files , we need use the files in our Web Service client project. Except the generated files, we also need other two files : stdsoap2.h, stdsoap2.cpp ( they are under the gSoap folder). We can use the following snippet of code :
#include "UserDetailServiceImplServiceSoapBinding.nsmap"
#include "soapUserDetailServiceImplServiceSoapBindingProxy.h"
const char endpoint[] = "http://172.16.23.25:9081/user-application-ws/UserDetail";
CReEngiApps GetApps(const char* userId)
{
CReEngiApps apps;
UserDetailServiceImplServiceSoapBindingProxy proxy(SOAP_XML_INDENT);
rso1__getAvailableApplications rso1__getAvailableApplications_;
rso1__getAvailableApplicationsResponse rso1__getAvailableApplicationsResponse_;
std::string user = userId;
rso1__getAvailableApplications_.arg0 = & user;
int ret = proxy.getAvailableApplications(endpoint, NULL, &rso1__getAvailableApplications_, &rso1__getAvailableApplicationsResponse_);
if (ret == SOAP_OK)
{
std::vector<rso1__application * > & applications = rso1__getAvailableApplicationsResponse_.return_;
apps.m_appNum = applications.size();
for (int i=0; i < apps.m_appNum; i++)
{
memcpy(apps.m_apps[i].m_szAppName, applications[i]->name->c_str(), strlen(applications[i]->name->c_str()));
memcpy(apps.m_apps[i].m_szAppUrl, applications[i]->url->c_str(), strlen(applications[i]->url->c_str()));
}
} else
{
apps.m_appNum = 0;
}
return apps;
}
ATL Server VS gSOAP
1, ATL Server is C++ template based.Its generated code has lots of template code. Template code is much harder than normal code to understand.gSOAP generates simple code, event only C code.
2, ATL Server has a fatal issue, we found, it generates incorrect SOAP content, which can cause request failed. So far, the code generated by gSOAP is good, no big issue.
Conclution
gSOAP is an open source tool can generate portable C/C++ Web Service proxy code.The generated code is easy to use.