Before I get into part II of my earlier post about Configuring and Debugging WCF Services, I want to write about a problem I solved recently. Took a lot of digging around and so I thought I would save someone time by making this post.
Disclaimer: I am primarily a Windows Developer. I usually don’t have any issues going between platforms, languages and such but by no means am I an expert on the Linux platform (or on SOAP for that matter).
The Motivation
The basic problem I was trying to solve was trying to hit a WCF service from Linux using C++. Why was I doing that you might ask. Well, in most environments out there today, programmers and architects have to make heterogeneous systems work together and deal with legacy systems that will not die (the reasons are many, and complicated).
Service Oriented Architectures come with the promise of making this integration between different platforms easy to achieve, maintain and evolve. However, choosing the technologies to implement SoA in is an important decision, as to be useful, the services you create should be accessible from every platform easily. Only then will you get the buy in needed for SoA to succeed.
gSOAP
gSOAP is a technology that allows you to create stubs for client and server side code from WSDLs. There is a lot more that gSOAP can do but I only used it for creating the client side stubs using the WSDL I got from my WCF service.
You can read all about it here.
Making it Work
OK, let’s get down to how we can use gSOAP to access a WCF service using a C++ client. The steps I had to take were
- Install the gSOAP library on my Linux box, in my local directory
- Generate the SOAP stubs for the target WCF service
- Create the client
Here are the steps again with many more details:
Install the gSOAP library
1. Download the gSOAP tar file. The website is here.
> wget http://sourceforge.net/projects/gsoap2/files/gSOAP/2.7.14%20stable%20%28update%29/gsoap_2.7.14.tar.gz/download
2. Untar the file.
> tar -xvzf packagename.tar.gz
3. Make and install. Run the following commands.
> ./configure
> make
> make install exec_prefix=$HOME // This is to get it installed on your local drive
Generate the SOAP stubs for the target service.
I installed gSOAP in my home directory. Also lets assume that the service I am trying to connect to exposed an endpoint with BasicHttpBinding, which boils down to simple SOAP. And it is located at the following address
http://www.myserver.com/myWCFservice.svc
and the method I am trying to invoke has the following signature:
int IMySevice.GetCount(string inputString)
// The service implements the IMyService interface and the interface has the GetCount method on it
You would need to execute the following commands to get your SOAP stubs:
Go to the appropriate directory for the platform you are on.
> cd <home dir>/lib/gSOAP/gsoap-2.7/gsoap/bin/linux386
Generate the WSDL header file.
> ./wsdl2h -o mywcfheader.h http://myserver.com/myWCFService.svc?wsdl
Generate the stub files by executing the gSOAP compiler
> ./soapcpp2 -I”<home dir>/lib/gSOAP/gsoap-2.7/gsoap/” mywcfheader.h
At this point you should have the stub files in you directory. As far as the client is concerned, you care about these files
|
File Name
|
Description
|
| BasicHttpBinding_IMyService.GetCount.req.xml |
Example SOAP Request |
| BasicHttpBinding_IMyService.GetCount..res.xml |
Example SOAP Response |
| soapBasicHttpBinding_IMyServiceProxy.h |
C++ Proxy that wraps the SOAP calls that are made on the client side into an Object Oriented interface. The client you write will consume this. |
| BasicHttpBinding_IMyService.nsmap |
File that defines the name space (for versioning of the SOAP protocol) of various schema prefixes |
| soapC.cpp, soapClient.cpp, soapClientLib.cpp, soapH.h and soapStub.h |
Client Side SOAP Code that the C++ proxy consumes |
Create the client.
The client code is pretty simple. It looks something like this:
// This will be the name of the proxy file created when
// you generate the stubs
#include “soapBasicHttpBinding_IMyServiceProxy.h”
// This will be the nsmap file created when you created
// the stubs
#include “BasicHttpBinding_IMyService.nsmap”
using namespace std;
int main()
{
// This will be the name of the service
// class in the proxy header file from above
BasicHttpBinding_IMyService s;
// This is the request and response that
// the service you are trying to call takes.
// Again you can find the types in the class
// used in the C++ proxy header
_ns1__GetCount req;
_ns1__GetCountResponse resp;
string is(”Hello There America”);
req.inputString = &is;
int err = s.__ns1__GetCount(&req, &resp);
if (SOAP_OK == err)
cout << “Service Returned: ” << *resp.GetCountResult << endl;
else
cout << “Error: ” << err << endl;
return 0;
}
2: // This will be the name of the proxy file created when
3: // you generate the stubs
4: #include “soapBasicHttpBinding_IMyServiceProxy.h”
5:
6: // This will be the nsmap file created when you created
7: // the stubs
8: #include “BasicHttpBinding_IMyService.nsmap”
9:
10: using namespace std;
11:
12: int main()
13: {
14: // This will be the name of the service
15: // class in the proxy header file from above
16: BasicHttpBinding_IMyService s;
17:
18: // This is the request and response that
19: // the service you are trying to call takes.
20: // Again you can find the types in the class
21: // used in the C++ proxy header
22: _ns1__GetCount req;
23: _ns1__GetCountResponse resp;
24:
25: string is(“Hello There America”);
26: req.inputString = &is;
27:
28: int err = s.__ns1__GetCount(&req, &resp);
29:
30: if (SOAP_OK == err)
31: cout << “Service Returned: ” <<
32: *resp.GetCountResult << endl;
33: else
34: cout << “Error: ” << err << endl;
35:
36: return 0;
37: }
38:
Since our binding is BasicHttpBinding, the WCF service expects SOAP 1.1 as the protocol. By default gSOAP 2.7 talks in SOAP 1.2. So we need to make sure that we make changes required to generate a client that will communicate using SOAP 1.1.
We can do this by changing the following in your *.nsmap and C++ Proxy header file. Change:
{“SOAP-ENV”, “http://www.w3.org/2003/05/soap-envelope”, “http://www.w3.org/2003/05/soap-envelope”, NULL},
{“SOAP-ENC”, “http://www.w3.org/2003/05/soap-encoding”, “http://www.w3.org/2003/05/soap-encoding”, NULL},
{“xsi”, “http://www.w3.org/2001/XMLSchema-instance”, “http://www.w3.org/*/XMLSchema-instance”, NULL},
{“xsd”, “http://www.w3.org/2001/XMLSchema”, “http://www.w3.org/*/XMLSchema”, NULL},
to this
{“SOAP-ENV”, “http://schemas.xmlsoap.org/soap/envelope/”, NULL, NULL},
{“SOAP-ENC”, “http://schemas.xmlsoap.org/soap/encoding/”, NULL, NULL},
{“xsi”, “http://www.w3.org/2001/XMLSchema-instance”, NULL, NULL},
{“xsd”, “http://www.w3.org/2001/XMLSchema”, NULL, NULL},
I found this information here.
Compile the client
Now we are ready to compile the client. Besides the client code you write and the stub files generated by gSOAP, we also need to supply the compiler with another file stdsoap2.cpp that comes with the installation. Also we need to add the gSOAP lib path to the include path so that it can pick up the appropriate libraries it needs.
The command will look like this:
g++ -I”<home directory>/lib/gSOAP/gsoap-2.7/gsoap” myclient.cpp soapC.cpp soapClient.cpp <home directory>/lib/gSOAP/gsoap-2.7/gsoap/stdsoap2.cpp
At this point things should work. Leave comments if something does not work for you or if there are other ways to do this.
Posted in web services Tagged: gsoap, linux, wcf