Back to Contents


I4GL Migration Guide


Migrate an I4GL web service provider to Genero

This section explains how to migrate a I4GL web service provider to a Genero application providing the same web service in order to let all clients, already accessing that service, unmodified (excepted for the hostname of course). Notice that the migration will be based on the soa zipcode demo in the I4GL package.

Step 1: Use the I4GL function and the I4GL .4cf configuration file

Use the I4GL .4cf configuration file to get all information about the I4GL web service

Example : The I4GL zipcode demo has following .4cf configuration file :

[SERVICE]
   TYPE               = publisher
   INFORMIXDIR        = /dbs/32bits/ifx/11.70.uc2
   DATABASE           = i4glsoa
   CLIENT_LOCALE      = en_US.8859-1
   DB_LOCALE          = en_US.8859-1
   INFORMIXSERVER     = ol_moscou1170uc2
   HOSTNAME           = moscou.strasbourg.4js.com
   PORTNO             = 9876
   I4GLVERSION        = 7.50.xC4
   WSHOME             = /dbs/32bits/ifx/11.70.uc2/AXIS2C
   WSVERSION          = AXIS1.5
   TMPDIR             = /tmp/zipcodedemo
   SERVICENAME        = ws_zipcode
   [FUNCTION]
       NAME           = zipcode_details
       [INPUT]
           [VARIABLE]NAME = pin TYPE = CHAR(10)[END-VARIABLE]
       [END-INPUT]
       [OUTPUT]
           [VARIABLE]NAME = city TYPE = CHAR(100)[END-VARIABLE]
           [VARIABLE]NAME = state TYPE = CHAR(100)[END-VARIABLE]
       [END-OUTPUT]
   [END-FUNCTION]
   [DIRECTORY]
       NAME           = /home/f4gl/fg/i4gl
       FILE           = soademo.4gl,
   [END-DIRECTORY]
[END-SERVICE]

Then simply copy your I4GL function without any modification into a new Genero file and add the Genero IMPORT com instruction at the beginning of the file.

Example : The I4GL soa demo contains the zipcode_details service (soademo.4gl)

IMPORT com

FUNCTION zipcode_details(pin)
       DEFINE state_rec RECORD
                pin CHAR(10),
                city CHAR(100),
                state CHAR(100)
              END RECORD,
              pin CHAR(10),
              sel_stmt CHAR(512);

       LET sel_stmt= "SELECT * FROM statedetails WHERE pin = ?";
       PREPARE st_id FROM sel_stmt;
       DECLARE cur_id CURSOR FOR st_id;
       OPEN cur_id USING pin;
       FETCH cur_id INTO state_rec.*;
       CLOSE cur_id;
       FREE cur_id;
       FREE st_id;
       RETURN state_rec.city, state_rec.state

END FUNCTION
Note : you may need some minor code modification for compatibility.

Step 2: Create a 4GL record for the input parameters

Add a new modular 4GL record where all members map to one of your I4GL web service input parameter, and keep the parameter order as defined in I4gl .4cf file.
You must then specify the web service input message name via the Genero XML attribute called XMLName, and assign it to the FUNCTION NAME as defined in the I4GL .4cf file.

Example : In the I4GL zipcode demo there is only one parameter: pin. So add following record at the beginning of the Genero file :

DEFINE zipcode_details_in RECORD ATTRIBUTE(XMLName="zipcode_details")
                   pin CHAR(10)
                      END RECORD
Note : Genero web services supports complex datatype as input parameters.

Step 3: Create a 4GL record for the output parameters

Add another modular 4GL record where all members map to one of your I4GL web service output parameter, and keep the parameter order as defined in I4GL .4cf file.
You must then specify the web service output message name via the Genero XML attribute called XMLName, and assign it to the FUNCTION NAME as defined in the I4GL .xcf file concatenated to response.

Example : In the I4GL zipcode demo there are two parameters: city and state. So add following record at the beginning of the Genero file:

DEFINE zipcode_details_out RECORD ATTRIBUTE(XMLName="zipcode_detailsresponse)
                city CHAR(100),
                state CHAR(100)
                       END RECORD
Note : Genero web services supports complex datatype as output parameters.

Step 4: Create a 4GL wrapper function

Create a Genero 4GL wrapper function without any parameters that will then use the input and output record created at Step 2 and 3 to call the I4GL function passing it the parameters retrieved from the records.

Example : In the I4GL zipcode demo there are 1 input and 2 output parameters. So the 4GL wrapper function must use these records to call the I4GL function as following :

FUNCTION zipcode_details_g()
  CALL zipcode_details(zipcode_details_in.pin) RETURNING zipcode_details_out.city,zipcode_details_out.state
END FUNCTION

Step 5: Publish the wrapper function as a Genero web service

Use the COM APIs to publish the I4GL function as a web service based on I4GL .4cf configuration file to get a compatible Genero web service.

To create a new 4GL function in charge of the service publication, you will need following elements of the I4GL .4cf configuration file:

Example : The I4GL zipcode demo has one function published as a Doc/Literal service.

FUNCTION create_zipcode_details_web_service()
  DEFINE serv com.WebService
  DEFINE op   com.WebOperation
  
  #
  # Create the web service based on the entries of the .4cf file
  #   SERVICENAME: The name of service is 'ws_zipcode'
  #   FUNCTION NAME: The namespace of the service is built from 
  #                  the base url 'http://www.ibm.com/' concatenated to 
  #                  the NAME of the I4GL function 'zipcode_details'
  #
  LET serv = com.WebService.CreateWebService("ws_zipcode","http://www.ibm.com/zipcode_details")
  
  
  #
  # Create and publish the Doc/Literal web function based on 
  #  step 2, step 3 and step 4 
  #  and from the FUNCTION NAME defined in the .4cf file
  #
  LET op = com.WebOperation.CreateDOCStyle("zipcode_details_g","zipcode_details",zipcode_details_in,zipcode_details_out)
  CALL serv.publishOperation(op,NULL)
  
  
  #
  # Register the service into the SOAP engine
  #
  CALL com.WebServiceEngine.RegisterService(serv)
    
END FUNCTION
Note 1: I4GL supports only Doc/Literal services.
Note 2: Genero web services can contain several 4GL functions in the same service. In other words, you can group several I4GL services into the same Genero service.

Step 6: Create the server

I4GL uses Axis as server for its services, but Genero has its own server programmable via the COM library. Create a new file and add the IMPORT com instruction at beginning of the server file, then simply create the main loop in 4GL that will process any incoming HTTP request.

The port of the service defined in the I4GL .4cf configuration file (via the PORTNO entry) can be reused by setting the FGLAPPSERVER environment variable to the same value before to run the server. However only on development or for tests, on production Genero Web services requires an application server called GAS in charge of load balancing. See the GAS documentation for more details about port configuration for deployment purpose.

Example : To migrate the I4GL zipcode demo, the service must be created in the server before run the main loop as following :

MAIN
  DEFINE ret INTEGER
  DEFER INTERRUPT
  
  # Create zipcode_details service
  CALL create_zipcode_details_web_service()

  # Start the server on port set in FGLAPPSERVER 
  #    (to be set to same value as PORTNO defined in the .4cf file)
  CALL com.WebServiceEngine.Start()
  
  # Handle any incoming request
  WHILE TRUE
    LET ret = com.WebServiceEngine.ProcessServices(-1)
    CASE ret
      WHEN 0
        DISPLAY "Request processed." 
      WHEN -1
        DISPLAY "Timeout reached."
      WHEN -2
        DISPLAY "Disconnected from application server."
        EXIT PROGRAM   # The Application server has closed the connection
      WHEN -3
        DISPLAY "Client Connection lost."
      WHEN -4
        DISPLAY "Server interrupted with Ctrl-C."
      WHEN -10
        DISPLAY "Internal server error."
    END CASE
    IF int_flag<>0 THEN
      LET int_flag=0
      EXIT WHILE
    END IF     
  END WHILE
END MAIN
Note : In Genero web service one server can contain several services. In other words, you can put all your I4GL services into one and the same server.

Step 7: Configure the database

Based on the DATABASE entry in the I4GL .4cf configuration file, use the Genero instruction to connect to the Informix database at server startup.

Example : In the I4GL zipcode demo the service access the database called : i4glsoa. So add following instruction at the beginning of the server file created as step 6:

DATABASE i4glsoa

MAIN
 ...
END MAIN

Step 8: Compile and run the Genero service

Then simply compile and link the 2 Genero files created above and run you Genero service. It will be directly available for any client, and provide the WSDL when requested via a HTTP GET with WSDL as query string.

Example : the Genero web service is accessible on URL: http://hostname:9876/ws_zipcode and can even more return the WSDL on URL: http://hostname:9876/ws_zipcode?WSDL.

$ fglcomp -M genero_service.4gl
$ fglcomp -M genero_server.4gl
$ fgllink -o genero_zipcode genero_service.42m genero_server.42m
$ export FGLAPPSEVER=9876
$ fglrun genero_zipcode.42r
Note 1 : The hostname depends on the machine your Genero application is started.
Note 2 : For deploying the service on production you will need the Genero application server (GAS) to load-balance the service. See the GAS documentation about Web Services when deployment is required.

Step 9: Disable Axis support of MTOM/XOP and WS-Addressing

I4GL is based on Axis web service for the SOAP layer and sends by default requests in MTOM/XOP and with support of WS-Addressing. However, Genero Web services doesn't support MTOM/XOP and WS-Addressing, therefore you have to unset both features on your Axis installation if you still want your I4GL client applications to communicate with the Genero web service after migration.

Example : The Axis installation contains a file called axis.xml where following two lines have to be removed:

    <parameter name="enableMTOM" locked="false">true</parameter>
    <module ref="addressing"/>

Back to the top


Migrate an I4GL web service consumer to Genero

This section explains how to migrate a I4GL web service consumer to a Genero application accessing the same web service. Notice that the migration will be based on the soa demo in the I4GL package.

Step 1: Generate the Genero web service stub from an I4GL WSDL

Use the I4GL WSDL located on the Axis server to generate the Genero web service client stub via the tool called fglwsdl.

Example : The WSDL file of the I4GL zipcode demo is located on $INFORMIXDIR/AXIS2C/services/ws_zipcode/zipcode_details.wsdl. So do following command:

$ fglwsdl -noFacets zipcode_details.wsdl
It will generate these two Genero files: Note : option -noFacets is required for this demo because the I4GL CHAR datatype will be generated as string in Genero what can lead to XML serialization error if not present.

Step 2: Modify the Genero .inc stubs to fix wrong I4GL WSDL

The I4GL WSDL contains namespace declaration for all I4GL web service datatypes, but in practice the I4GL axis server doesn't care about namespaces, but Genero does. So you have to open the generated Genero .inc file and remove all attributes called XMLNamespace and XSTypeNamespace.

Example : The generated .inc file from the I4GL WSDL must be modified as following:

#-------------------------------------------------------------------------------
# File: ws_zipcode_zipcode_detailsservice.inc
# GENERATED BY fglwsdl 101601
#-------------------------------------------------------------------------------
# THIS FILE WAS GENERATED. DO NOT MODIFY.
#-------------------------------------------------------------------------------

GLOBALS

...

#
# TYPE : tzipcode_details
#
TYPE tzipcode_details RECORD ATTRIBUTE(XMLSequence,XSTypeName="zipcode_details") #,XSTypeNamespace="http://www.ibm.com/zipcode_details")
  pin STRING ATTRIBUTE(XMLName="pin")
END RECORD
#-------------------------------------------------------------------------------

#
# TYPE : tzipcode_detailsresponse
#
TYPE tzipcode_detailsresponse RECORD ATTRIBUTE(XMLSequence,XSTypeName="zipcode_detailsresponse") #,XSTypeNamespace="http://www.ibm.com/zipcode_details")
  city STRING ATTRIBUTE(XMLName="city"),
  state STRING ATTRIBUTE(XMLName="state")
END RECORD

...

#-------------------------------------------------------------------------------
#
# Operation: zipcode_details
#
# FUNCTION: zipcode_details_g()
#   RETURNING: soapStatus
#   INPUT: GLOBAL zipcode_details
#   OUTPUT: GLOBAL zipcode_detailsresponse
#
# FUNCTION: zipcode_details(p_pin)
#   RETURNING: soapStatus ,p_city ,p_state
#
# FUNCTION: zipcode_detailsRequest_g()
#   RETURNING: soapStatus
#   INPUT: GLOBAL zipcode_details
#
# FUNCTION: zipcode_detailsResponse_g()
#   RETURNING: soapStatus
#   OUTPUT: GLOBAL zipcode_detailsresponse
#

#-------------------------------------------------------------------------------

# VARIABLE : zipcode_details
DEFINE zipcode_details tzipcode_details ATTRIBUTE(XMLName="zipcode_details") #,XMLNamespace="http://www.ibm.com/zipcode_details")


#-------------------------------------------------------------------------------

# VARIABLE : zipcode_detailsresponse
DEFINE zipcode_detailsresponse tzipcode_detailsresponse ATTRIBUTE(XMLName="zipcode_detailsresponse") #,XMLNamespace="http://www.ibm.com/zipcode_details")

END GLOBALS
Note : Genero web services provides a lots of XML mapping attributes.

Step 3: Include the generated stub into your I4GL application

Add in all I4GL files calling a web service the generated .inc stub with a GLOBALS instruction.

Example : In the I4GL zipcode demo, only the clsoademo.4gl file uses web services. So add following line at beginning of the file :

GLOBALS "ws_zipcode_zipcode_detailsservice.inc"

MAIN
  ...
END MAIN
Note : This allows access to the Genero global variables and datatypes used in the web service call, so as the Genero global wsError record to retrieve error codes if any.

Step 4: Modify the I4GL web service function call

The Genero web service function name is defined in the generated .4gl file and must be used instead of the I4GL function name.

Example : In the I4GL zipcode demo, the web service function name is cons_ws_zipcode and must be renamed to zipcode_details as following:

FUNCTION func_cons_ws_zipcode()
   DEFINE state_rec RECORD
                pin CHAR(10),
                city CHAR(100),
                state CHAR(100)
              END RECORD;
   #           
   # Genero web service status returning 
   #  whether web function call was successful or not
   #
   DEFINE soapstatus INTEGER    

   #
   # I4GL web service function name is 'cons_ws_zipcode'
   # CALL cons_ws_zipcode("97006") returning state_rec.city, state_rec.state   
   
   
   # Genero web service function name is 'zipcode_details'
   CALL zipcode_details("97006") returning soapstatus, state_rec.city, state_rec.state
   ...
END FUNCTION
Note : In Genero web service there is an additional returned parameter : soapstatus. If it contains 0 the operation was a success, otherwise an error occurred.

Step 5: Handle Genero web services errors

I4GL web service errors are returned on a non conventional SOAP fault what cannot be handled in Genero. However the errors are handled through the additional returned parameter soapstatus that must be checked after each web service call. If its value is not zero, an error has occurred and can be retrieved via the global Genero wsError record defined in the above generated .inc file.

Example : In Genero web service you must check the soap status after each web service call as following:

FUNCTION func_cons_ws_zipcode()
   DEFINE state_rec RECORD
                pin CHAR(10),
                city CHAR(100),
                state CHAR(100)
              END RECORD;
   #           
   # Genero web service status returning 
   #  whether web function call was successful or not
   #
   DEFINE soapstatus INTEGER    
   
   # Genero web service function call
   CALL zipcode_details("97006") returning soapstatus, state_rec.city, state_rec.state
   # Check soap status for errors after zipcode_details call
   IF soapstatus<>0 THEN
     # Display error information from the server
     DISPLAY "Error:"
     DISPLAY "  code :",wsError.code
     DISPLAY "  ns   :",wsError.codeNS
     DISPLAY "  desc :",wsError.description
     DISPLAY "  actor:",wsError.action
   ELSE   
     # Display results
     DISPLAY "\n ------------------------- \n"
     DISPLAY "SUPPLIED ZIP CODE: 97006 \n"
     DISPLAY " ------------------------- \n"
     DISPLAY "RESPONSE FROM WEB SERVICE \n"
     DISPLAY " ------------------------- \n"
     DISPLAY " CITY:",state_rec.city
     DISPLAY "\n STATE:",state_rec.state
     DISPLAY "\n ======================== \n"
   END IF
   ...
END FUNCTION

Step 6: Compile and run the Genero client

Then simply compile your modified I4GL application for Genero and execute it. Your application will then connect to the web service passing and returning the parameters as it were only simple 4GL function calls.

Example : To compile your I4GL web service application for Genero you must do following commands:

$ fglcomp -M ws_zipcode_zipcode_detailsservice.4gl
$ fglcomp -M clsoademo.4gl
$ fgllink -o clsoademo.42r clsoademo.42m ws_zipcode_zipcode_detailsservice.42m
$ fglrun clsoademo.42r

Step 7: Disable Axis support of MTOM/XOP and WS-Addressing

I4GL is based on Axis web service for the SOAP layer and sends by default requests in MTOM/XOP and with support of WS-Addressing. However, Genero Web services doesn't support MTOM/XOP and WS-Addressing, therefore you have to unset both features on your Axis installation if you want your Genero client application to communicate with an I4GL web service provider.

Example : The Axis installation contains a file called axis.xml where following two lines have to be removed:

    <parameter name="enableMTOM" locked="false">true</parameter>
    <module ref="addressing"/>

Remark: Standalone Axis server is buggy

The I4GL standalone axis server adds an extra CR LF after the body of the SOAP HTTP post response what leads the Genero client to return the error message : Body content bigger than expected. This is not allowed as defined in HTTP RFC2616.

Notice however that Axis works as expected if loaded from Apache server.

Back to the top