Section 20.3: gSOAP Up Chapter 20: Web Services Chapter 21: Revisions 

20.4 wsdl2fgl

The Aubit4GL tool wsdl2fgl generates 4GL programs and calls the gSOAP tools to generate the SOAP .c files needed to access web services.
To create a 4GL web client, you can call wsdl2fgl with either a WSDL file or a gSOAP generated .h file.
wsdl2fgl service.wsdl will run wsdl2h to create the header file service.h first then run soapcpp2 -c
wsdl2fgl service.h will run soapcpp2 -c (without the need to run gSOAP’s wsdl2h)
wsdl2fgl will generate a set of files:
Check the generated file: Client_4gl.c and look for which functions are available - they will be of the form:
aclfgl___ns2__funcname( ... )
where aclfgl_ is the usual prefix that 4GL applies to 4GL function names to avoid namespace clashes with standard C library calls. Put appropriate CALL statements into a MAIN ... END MAIN function using the appropriate arguments and link your main module with all the above stuff to create your 4GL web client. In your 4GL code omit the aclfgl_ prefix that you find in the Client_4gl.c file. The __ns2__ or __ns3__ or whataver are gSOAP generated function prefixes similar in spirit to Aubit4GL’s aclfgl_ prefix.

20.4.1 Client Example

For a real world example, download the wsdl file from
http://xmethods.net/ve2/ViewListing.po?key=427560
and save the file as driving.wsdl
Now run the command: wsdl2fgl driving.wsdl
Looking at the file: Client_4gl.c we find:
 aclfgl___ns2__getdirections (                      
      // Parameter 1 - fromAddress CHAR(...)    
      // Parameter 2 - toAddress CHAR(...)                
      // Parameter 3 - distanceUnit CHAR(...)            
      // Parameter 4 - expresswayEnabled CHAR(...)
)

   
 aclfgl___ns3__getdirections (             
      // Parameter 1 - fromAddress CHAR(...)         
      // Parameter 2 - toAddress CHAR(...)         
      // Parameter 3 - distanceUnit CHAR(...)               
      // Parameter 4 - expresswayEnabled CHAR(...)
)
One is calling via service name drivingSOAP, the other via drivingSOAP2.
So, using the first one with applicable parameters we can write a file (say main.4gl):
MAIN 
   DEFINE lv_data char(1024)

   CALL __ns2__getdirections(
      ’1600 Amphitheatre PArkway Mountain View, CA, USA’
      ’2775 Middlefield Road Palo Alto, CA, USA’,
      ’miles’,
      ’true’)
   RETURNING lv_data
   DISPLAY lv_data CLIPPED
END MAIN 
Note: The Client_4gl.c file has a function declaration for
aclfgl__ns2__getdirections()
The Aubit4GL compiler 4glpc prepends the acl4gl_ to all 4GL function names when translating to C. So we have to omit the the aclfgl_ prefix from our 4GL CALL statement. Rest assured that the 4glc program will supply the missing aclfgl_ prefix to access the C function.
(The output gives directions from Google HQ to a Starbucks close by)
Now - to compile it all up :
4glpc -o client.4ae main.4gl \
   soapClient.c soapC.c \
   Client_4gl.c -lgsoap

20.4.2 Web Server

In order to export a function, a declaration of the function has to be created and used. This can be done using the fglproto program. First create a datafile containing your 4gl definition with :
4glpc -t WRITE somemod.4gl
You can do this with multiple 4GL modules. Just run the command for each 4gl module. At this stage though, you only need to specify modules which
  1. Contain functions you wish to export
  2. Contain functions used directly in RETURN statements for functions you are exporting
You may wish to consolidate the functions into a single module - or create a wrapper function to call your real functions so you don’t export all of the functions in a complex 4gl!
Remember - here we are wanting to create stub functions to manage the Web service. Later we will need the modules containing all the other functions that are needed to link our application together, but that is not at this stage ...
After you have run 4glpc -t WRITE for each module, we can generate the stub functions for all non-local functions in our 4gl module. We do this with fglproto:
fglproto -w somemod
If you have specified more than one 4gl module - add more to that one :
fglproto -w somemod1 somemod2
Remember - here we will create the logic to export all of the functions not marked as local - you don’t need to use all the modules for the application, you can omit any that are needed just for linking..
This should generate some files : prototypes_server.c prototypes_client.c blacklist prototypes.h
(You can ignore blacklist completely). Now, in order to create a server you need to use soapcpp2 to process our prototypes.h file :
soapcpp2 -c prototypes.h
(We’re using normal C generation so we need to use the -c option to tell soapcpp2 not to generate C++ code)
That should generate us some more files ;-)
For our server - we will need to use the soapcpp2 generated C files :
soapC.c soapServerLib.c prototypes_server.c
You will also need a function to start the server itself - you can use this basic one as a starting point (see tools/test/gsoap/server.c) :
#include "soapH.h" 
/* get the gSOAP-generated definitions */ 
#include "fglserver.nsmap" 
/* get the gSOAP-generated namespace bindings */ 
#include <math.h>

int aclfgl_run_server(int n) 
{ int m, s; /* master and slave sockets */ 
   int port; 
   struct soap *soap = soap_new(); 
   if (n==0) 
   { soap_serve(soap); /* serve as CGI application */ 
     soap_done(soap); 
     free(soap); 
     return 0; 
   }
   port=A4GL_pop_int(); 
   printf("Listening on port %d\n",port); 
   m = soap_bind(soap, NULL, port, 100); 
   /* bind to the port supplied as command-line arg */ 
   if (m < 0) 
   { 
      soap_print_fault(soap, stderr); 
      exit(-1); 
   } 
   fprintf(stderr, 
    "Socket connection successful: master socket = %d\n",
    m);
   for (;;) 
   { 
      s = soap_accept(soap); 
     fprintf(stderr, 
      "Socket connection successful: slave socket = %d\n",
      s);
     if (s < 0) 
     { soap_print_fault(soap, stderr); 
       exit(1); 
     } 
     soap_serve(soap); 
     soap_end(soap); 
   }
  soap_done(soap); 
  free(soap); 
  return 0; 
} 
You can call this function from within 4gl with something like:
main 
   call run_server(9090) 
end main
Putting it all together ...
Assuming you’ve put
4glpc -o server \
   soapC.c soapServerLib.c \
   server.c functions.o \
   prototypes_server.c \
   main.4gl 
   <all your 4gl modules> \
  -lgsoap
Eg. if your module containing functions you want to serve is called functions.4gl’ but needs subrouts.4gl to link :
4glpc -t WRITE functions.4gl 
fglproto -w functions 
soapcpp2 -n -c prototypes.h 
4glpc -o functions.o functions.4gl 
4glpc -o subrouts.o subrouts.4gl 
4glpc -g -o server \
   soapC.c soapServerLib.c \
   server.c \
   functions.o subrouts.o \
   main.4gl \
   prototypes_server.c \
   -lgsoap 
Now - if you want to create a client to use the webservices for these functions - its very straightforward.
You simply need to compile your 4gl along with the generated client code and the gsoap library, and optionally alter the function to include the URL where they will be served.
4glpc -g -o client.4ae client_4gl \
   soapC.c soapClient.c \
   prototypes_client.c \
   <your 4gls> \
   -lgsoap 
The functions can have an optional first parameter specifying the URL where the functions will be served from. This is a default of
http://localhost:9090
if not specified, but the default can be manually changed in the prototypes.h generated by fglproto -w
eg.
main 
   call get_tabname(54321) 
    returning lv_tabname 
   call get_tabname("http://localhost:9090",54321) 
    returning lv_tab name 
    display lv_tabname,":" 
end main 

This assumes that you have exported a function similar to :

database test1
main 
    call run_server(9090) 
end main
function get_tabname(lv_id) 
   define lv_tabname char(18) 
   define lv_id integer

   display "lv_id=",lv_id
   select tabname into lv_tabname 
      from systables 
      where tabid=lv_id
   return lv_tabname
end function
See the code in tools/test/gsoap for this example.....

20.4.3 Limitations

20.4.3.1 Single Threaded

All functions are served from what should be thought of as a single threaded application.
There is every likelihood though that you will not connect back to the service which you have previously called. No state will be preserved etc. You should therefore consider function calls to be atomic. For example, you cannot declare a cursor in one web service call, then read it in another. That cursor might well not exist when the 2nd call runs.

20.4.3.2 Limited Datatypes

We cannot handle arrays.

20.4.3.3 Unsupported Services

If there is a particular web service that you want to use which cannot be compiled, please pass on the details to:
support@aubit.com
 Section 20.3: gSOAP Up Chapter 20: Web Services Chapter 21: Revisions