To access a remote Web Service, you first must get the WSDL information from the service provider. Sample services can be found through UDDI registries (http://www.uddi.org), or on other sites such as XMethods (http://www.xmethods.net).
Using the fglwsdl tool, you can obtain the WSDL information for a GWS Client application. The following example requests the Calculator Web Service information from the specified URL, and the output files will have the base name ws_calculator:
fglwsdl -o ws_calculator http://localhost:8090/Calculator?WSDL
For a client application, fglwsdl generates two output files, which should not be modified:
This file must be listed in a GLOBALS statement at the top of any .4gl modules that you write for your GWS Client application.
This file must be compiled and linked into your GWS Client application.
Check this structure, defined in the globals .inc file, for a
detailed error description when a Web Service function returns with a non-zero
status.
Notice that even if status is -2 due to asynchronous calls, the record
contains a description.
DEFINE wsError RECORD code STRING, -- short description of the error codeNS STRING, -- the namespace of the error code description STRING, -- description of the error action STRING -- internal "SOAP action" END RECORD
Genero Web Services client functions have the following requirements:
As a result, two types of GWS functions are generated for the Web Service operation that you requested:
The generated .inc globals file contains comments that list the prototypes of the functions for the GWS operation, and the definitions of the global INPUT and OUTPUT records.
More and more Web Services provide support of the different WS-* specifications. To enable a better interoperability with such services, the fglwsdl tool allows the programmer to modify the SOAP request before it is sent, and to perform additional verifications of the SOAP response before it is returned from the 4GL function.
When option -domHandler is used, the fglwsdl tool performs the following two operations
at once:
FUNCTION ServiceName_HandleRequest(operation,doc,header,body) DEFINE operation STRING -- Operation name of the request to be modified. DEFINE doc xml.DomDocument -- Entire XML document of the request DEFINE header xml.DomNode -- XML node of the SOAP header of the request DEFINE body xml.DomNode -- XML node of the SOAP body of the request CASE operation WHEN "Add" ... -- Use the DOM APIs to modify the request of the Add operation WHEN "Sub" ... -- Use the DOM APIs to modify the request of the Sub operation OTHERWISE DISPLAY "No modification for operation :",operation END CASE RETURN TRUE -- Continue normally in Add_g() or Sub_g() END FUNCTION
FUNCTION ServiceName_HandleResponse(operation,doc,header,body) DEFINE operation STRING -- Operation name of the response to be checked. DEFINE doc xml.DomDocument -- Entire XML document of the response DEFINE header xml.DomNode -- XML node of the SOAP header of the response DEFINE body xml.DomNode -- XML node of the SOAP body of the response CASE operation WHEN "Add" ... -- Use the DOM APIs to check the response of the Add operation WHEN "Sub" ... -- Use the DOM APIs to check the response of the Sub operation OTHERWISE DISPLAY "No verification for operation :",operation END CASE RETURN TRUE -- Continue normally in Add_g() or Sub_g() END FUNCTION
FUNCTION ServiceName_HandleResponseFault(operation,doc,header,body) DEFINE operation STRING -- Operation name of the fault response to be checked. DEFINE doc xml.DomDocument -- Entire XML document of the fault response DEFINE header xml.DomNode -- XML node of the SOAP header of the fault response DEFINE body xml.DomNode -- XML node of the SOAP body of the fault response CASE operation WHEN "Add" ... -- Use the DOM APIs to verify the SOAP fault response of the Add operation WHEN "Sub" ... -- Use the DOM APIs to verify the SOAP fault response of the Sub operation OTHERWISE DISPLAY "No verification for operation :",operation END CASE RETURN TRUE -- Continue normally in Add_g() or Sub_g() END FUNCTION
The example Web Service for which the WSDL information was requested, Calculator, has an Add operation that returns the sum of two integers.
The generated file ws_calculator.inc lists the prototype for the Add and Add_g functions, the asynchronous AddRequest_g and AddResponse_g functions, as well as the definitions of the global variables Add and AddResponse:
# Operation: Add
#
# FUNCTION: Add_g() -- Function that uses the global input and output records # RETURNING: soapStatus -- An integer where 0 represents success # INPUT: GLOBAL Add # OUTPUT: GLOBAL AddResponse # # FUNCTION: Add(p_a, p_b) -- Function with input parameters that correspond # RETURNING: soapStatus ,p_r -- to the a and b variables of the global
-- INPUT record
-- Return values are the status integer and the value
-- in the r variable of the global OUTPUT record # # FUNCTION: AddRequest_g() -- Asynchronous function that uses the global input record # RETURNING: soapStatus -- An integer where 0 represents success, -1 error # INPUT: GLOBAL Add -- and -2 means that a previous request was sent and that a response is in progress. # # FUNCTION: AddResponse_g() -- Asynchronous function that uses the global output record # RETURNING: soapStatus -- An integer where 0 represents success, -1 error # OUTPUT: GLOBAL AddResponse -- and -2 means that the response was not yet received, and that a new call should be done later.
#VARIABLE : Add -- defines the global INPUT record DEFINE Add RECORD ATTRIBUTE(XMLName="Add",
XMLNamespace="http://tempuri.org/")
a INTEGER ATTRIBUTE(XMLName="a",XMLNamespace=""),
b INTEGER ATTRIBUTE(XMLName="b",XMLNamespace="")
END RECORD
# VARIABLE : AddResponse -- defines the global OUTPUT record DEFINE AddResponse RECORD ATTRIBUTE(XMLName="AddResponse",
XMLNamespace="http://tempuri.org/")
r INTEGER ATTRIBUTE(XMLName="r",XMLNamespace="")
END RECORD
The information obtained from the ws_calculator.inc file allows you to write code in your own .4gl module as part of the Client application, using the Web Service operation Add.
Since the input variables for our example are simple integers, you can call the Add function in your Client application, defining variables for the parameters and return values.
FUNCTION myWScall()
DEFINE op1 INTEGER DEFINE op2 INTEGER DEFINE result INTEGER DEFINE wsstatus INTEGER ... LET op1 = 6
LET op2 = 8
CALL Add(op1, op2) RETURNING wsstatus, result
...
DISPLAY result
You could choose to call the Add_g function instead, using the global records Add and AddResponse directly. If the input variables are complex structures like records or arrays, you are required to use this function.
FUNCTION myWScall()
DEFINE wsstatus INTEGER
...
LET Add.a = 6
LET Add.b = 8
LET wsstatus = Add_g()
...
DISPLAY AddResponse.rIn this case, the status is returned by the function, which has also put the result in the AddResponse global record.
See Tutorial: Writing a Client Application for more information. The demo/WebServices subdirectory of your Genero installation directory contains complete examples of Client Applications.
If you don't want your application to be blocked when waiting for the response to a request, you should first call AddRequest_g; this will send the request using the global Add record to the server. It returns a status of 0 (zero) if everything goes well, -1 in case of error, or -2 if you tried to resend a new request before the previous response was retrieved.
FUNCTION sendMyWScall()
DEFINE wsstatus INTEGER
...
LET Add.a = 6
LET Add.b = 8
LET wsstatus = AddRequest_g()
IF wstatus <> 0 THEN DISPLAY "ERROR :", wsError.code
END IF
...You can then call AddResponse_g to retrieve the response in the AddResponse global record of the previous request. If returned status is 0 (zero) the response was successfully received, -1 means that there was an error, and -2 means that the response was not yet received and that the function should be called later.
FUNCTION retrieveMyWScall()
DEFINE wsstatus INTEGER
...
LET wsstatus = AddResponse_g()
CASE wstatus
WHEN -2
DISPLAY "No response available, try later"
WHEN 0
DISPLAY "Response is :",AddResponse.r
OTHERWISE DISPLAY "ERROR :", wsError.code
END CASE ...You can mix the asynchronous call with the synchronous one as they are using two different requests. In other words, you can perform an asynchronous request with AddRequest_g, then a synchronous call with Add_g, and then retrieve the response of the previous asynchronous request with AddResponse_g.
Warning: In development mode, a single 4GL Web Service server can only handle one request at a time, and several asynchronous requests in a row without retrieving the corresponding response will lead to a deadlock. To support several asynchronous requests in a row, it is recommended that you are in deployment mode with a GAS as the front end.