Knacker
Knacker

Reputation: 31

gSOAP server with multiple wsdl from ONVIF specifications

I'm trying to create a web service in C++ using gSOAP. I generated a couple of headers from ONVIF wsdl:

wsdl2h -x -o dm.h http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl -tC:\gsoap-2.8\gsoap\typemap.dat
wsdl2h -x -o an.h http://www.onvif.org/onvif/ver20/analytics/wsdl/analytics.wsdl -tC:\gsoap-2.8\gsoap\typemap.dat

After that I compiled the headers generating C++ service proxies and objects:

soapcpp2.exe -j -S dm.h -IC:\gsoap-2.8\gsoap\import;C:\gsoap-2.8\gsoap -x -qDm
soapcpp2.exe -j -S an.h -IC:\gsoap-2.8\gsoap\import;C:\gsoap-2.8\gsoap -x -qAn

in order to obtain an application similar to the one described by gSOAP documentation: http://www.cs.fsu.edu/~engelen/soapdoc2.html#tth_sEc7.2.8

Compiling in Visual Studio, i get the following errors:

error C3861: 'soap_in_PointerToSOAP_ENV__Fault': identifier not found
error C3861: 'soap_in_PointerToSOAP_ENV__Header': identifier not found
error C3861: 'soap_out_PointerToSOAP_ENV__Fault': identifier not found
error C3861: 'soap_out_PointerToSOAP_ENV__Header': identifier not found

I tried to follow what the documentation says (http://www.cs.fsu.edu/~engelen/soapdoc2.html#tth_sEc19.35) about the serializers not being compiled at all because of the definition WITH_NOGLOBAL and compiled an empty env.h file, but that didn't solve the problem.

I searched the web but I can't find any solution. The problem seems related not to the Header and Fault themselves but the the pointers. Right? What should I do?

Upvotes: 3

Views: 3093

Answers (3)

Dr. Alex RE
Dr. Alex RE

Reputation: 1708

Here is the solution.

  1. Run wsdl2h -x -Nan -o an.h http://www.onvif.org/onvif/ver20/analytics/wsdl/analytics.wsdl. Note that I'm using option -Nan here to let soapcpp2 generate two service/proxy classes for the two services defined by the two bindings in this WSDL. Otherwise you get one service/proxy class that combines the two together in one class, which is not elegant (but works either way).
  2. Run wsdl2h -x -o dm.h http://www.onvif.org/onvif/ver20/analytics/wsdl/analytics.wsdl
  3. Create a new file env.h
  4. Edit env.h and add #import "xop.h" and #import "wsa5.h". Basically, you want all plugin-based headers to be imported here, since the plugins must work with global (non-C++-namespaced) structures. Also, the env.h file can be customized by adding a struct SOAP_ENV__Header and/or a struct SOAP_ENV__Detail structure. This is needed to exchange SOAP Headers and specific SOAP Fault details. In this case that won't be needed, since #import "wsa5.h" defines our SOAP Headers already for WS-Addressing.
  5. Run soapcpp2 -penv env.h to generate the global SOAP Header and SOAP Fault serialization code (envStub.h, envH.h and envC.cpp).
  6. Edit an.h and add the following definitions for SOAP Header and Faults

an.h:

struct SOAP_ENV__Header_
{
  // place SOAP Header elements here, if any
};
struct SOAP_ENV__Fault_
{
  char *faultcode;
  char *faultstring;
  char *faultactor;
  struct SOAP_ENV__Detail_ *detail;
  struct SOAP_ENV__Code_ *SOAP_ENV__Code;
  struct SOAP_ENV__Reason_ *SOAP_ENV__Reason;
  char *SOAP_ENV__Node;
  char *SOAP_ENV__Role;
  struct SOAP_ENV__Detail_ *SOAP_ENV__Detail;
};
struct SOAP_ENV__Detail_
{
  char *__any;
  int __type;
  void *fault;
};
struct SOAP_ENV__Code_
{
  char *SOAP_ENV__Value;
  struct SOAP_ENV__Code_ *SOAP_ENV__Subcode_;
};
struct SOAP_ENV__Reason_
{
  char *SOAP_ENV__Text;
};
  1. In an.h change the line struct SOAP_ENV__Envelope { struct SOAP_ENV__Header_ *SOAP_ENV__Header; _XML SOAP_ENV__Body; }; by using struct SOAP_ENV__Header_ instead of struct SOAP_ENV__Header.
  2. In an.h change line struct SOAP_ENV__Fault_* Fault by using struct SOAP_ENV__Fault_ instead of struct SOAP_ENV__Fault.
  3. Repeat the steps 6-8 for dm.h.
  4. Now you can use C++ namespaces, so run soapcpp2 -j -x -qAn -I $GSOAPPATH/gsoap/import -I $GSOAPPATH/gsoap an.h to generate C++ service and proxy classes in the An namespace (with option -qAn).
  5. Run soapcpp2 -j -S -x -qDm -I $GSOAPPATH/gsoap/import -I $GSOAPPATH/gsoap dm.h.
  6. Since we're using the duration plugin (this custom serializer is optional, edit typemap.dat to remove it if desired and then rerun wsdl2h), copy $GSOAPPATH/custom/duration.c to local duration.cpp (note the .cpp extension, since MSVC++ does not like mixing .c with .cpp sources).
  7. Edit duration.cpp and change #include "soapH.h" to #include "anH.h".
  8. Then compile the whole thing with your code, e.g. client-side c++ yourclientapp.cpp DmC.cpp AnC.cpp envC.cpp DmDeviceBindingProxy.cpp AnRuleEngineBindingProxy.cpp AnAnalyticsEngineBindingProxy.cpp stdsoap2.cpp dom.cpp duration.cpp or server-side c++ yourserviceapp.cpp DmC.cpp AnC.cpp envC.cpp DmDeviceBindingService.cpp AnRuleEngineBindingService.cpp AnAnalyticsEngineBindingService.cpp stdsoap2.cpp dom.cpp duration.cpp.
  9. If you're using the WS-Addressing plugin, also compile $GSOAPPATH/gsoap/plugin/wsaapi.c with your code (but rename wsaapi.c to wsaapi.cpp since MSVC++ does not like mixing .c with .cpp sources).

EDIT

You can skip steps 6-8 if you change typemap.dat as follows, then rerun wsdl2h with the modified typemap.dat:

SOAP_ENV__Envelope  = struct SOAP_ENV__Envelope { struct SOAP_ENV__Header_ *SOAP_ENV__Header; _XML SOAP_ENV__Body; }; | struct SOAP_ENV__Envelope
SOAP_ENV__Header    = struct SOAP_ENV__Header_ { /* place SOAP Header elements here, if any */ }; | struct SOAP_ENV__Header_
SOAP_ENV__Fault     = \
struct SOAP_ENV__Fault_\
{\
  char *faultcode;\
  char *faultstring;\
  char *faultactor;\
  struct SOAP_ENV__Detail_ *detail;\
  struct SOAP_ENV__Code_ *SOAP_ENV__Code;\
  struct SOAP_ENV__Reason_ *SOAP_ENV__Reason;\
  char *SOAP_ENV__Node;\
  char *SOAP_ENV__Role;\
  struct SOAP_ENV__Detail_ *SOAP_ENV__Detail;\
};\
struct SOAP_ENV__Detail_\
{\
  char *__any;\
  int __type;\
  void *fault;\
};\
struct SOAP_ENV__Code_\
{\
  char *SOAP_ENV__Value;\
  struct SOAP_ENV__Code_ *SOAP_ENV__Subcode_;\
};\
struct SOAP_ENV__Reason_\
{\
  char *SOAP_ENV__Text;\
};\
| struct SOAP_ENV__Fault_

Upvotes: 2

yudi-matsuzake
yudi-matsuzake

Reputation: 101

I faced this issue and almost lost my mind trying to find the solution. I noticed that a directive using #ifndef WITH_NOIDREF encapsulates some of soap_(out|in)_PointerToSOAP_ENV__(Fault|Header) indentifiers. Then when compile with the WITH_NOIDREF defined the erros messages decreases a little. So I manually encapsulates all the soap_(out|in)_PointerToSOAP_ENV__(Fault|Header) identifiers and it worked!

Example:

#ifndef WITH_NOIDREF
/// The soap_(out|in)_PointerToSOAP_ENV__(Fault|Header) indentifiers
// ...

#endif //WITH_NOIDREF

I'm afraid you are using windows, but I made some scripts to automate the whole process in:

https://github.com/yudi-matsuzake/onvif-mkmakefile

Upvotes: 0

mpromonet
mpromonet

Reputation: 11963

The code generated using the -q option seems to have a namespace problem with soap_in_PointerToSOAP_ENV__Fault and soap_in_PointerToSOAP_ENV__Header. The same problem occurs also with only 1 wsdl.

An other approach is to use the -p option like this :

soapcpp2.exe -j -S dm.h -IC:\gsoap-2.8\gsoap\import;C:\gsoap-2.8\gsoap -x -pDm
soapcpp2.exe -j -S an.h -IC:\gsoap-2.8\gsoap\import;C:\gsoap-2.8\gsoap -x -pAn

Upvotes: 0

Related Questions