Back to Contents 


Writing a GWS Server application

This tutorial guides you through the steps to create a Server application for a Genero Web Service that can be accessed over the web by Client applications. A complete example is in the demo/WebServices subdirectory of the Genero installation directory.

You can write your Server application based on input/output records that you have defined. Or, you can use the tool fglwsdl to include third-party WSDL information in your Server application.

 Topics

See also: Choosing a Web Services Style, The fglwsdl Tool, Deployment


Including the Genero Web Services library

The methods associated with creating and publishing a Web Service are contained in the classes that make up the Genero Web Services Library (com). Include the following line at the top of each module of your GWS Server application to import the library:

IMPORT com

Back to the top


Example 1: Writing the entire Server Application

You can define a Web Service in your application and write definitions for the input and output records that will be used by the Service. This example illustrates a Service that has one operation, Add, to provide the sum of two numbers.

Step 1: Define Input and Output Records

Based on the desired functionality of the operations that you plan for the Service, define the input and output records for each operation. BDL functions that are written to implement a Web Service operation cannot have input parameters or return values. Instead, each function's input and output message must be defined as a global or module RECORD.

The Input message

The fields of the global or module record represent each of the input parameters of the Web Function. The name of each field in the record corresponds to the name used in the SOAP request. These fields are filled with the contents of the SOAP request by the Web Services engine just before executing the corresponding BDL function.

The Output message

The fields of the global or module record represent each of the output parameters of the Web Function. The name of each field in the record corresponds to the name used in the SOAP request. These fields are retrieved from the Web Services engine immediately after executing the BDL function, and sent back to the client.

Your GWS service has one planned operation that adds two integers and returns the result. The input and output records are defined as follows:

GLOBALS
  DEFINE 
    add_in RECORD   # input record
      a INTEGER,
      b INTEGER 
    END RECORD, 
    add_out RECORD  # output record
       r INTEGER
    END RECORD
END GLOBALS

Step 2: Write a BDL function for each Service operation

You will need to write a function to implement each operation, using the input and output global records. 

To implement your Add operation:

#User Public Functions

FUNCTION add()
 LET add_out.r = add_in.a + add_in.b
END FUNCTION

Step 3: Create the Service and Operations

The Genero Web Services library (com) provides classes and methods that allow you to use Genero BDL to configure a Web Service and its operations. 

Define variables for the WebService and WebOperation objects

FUNCTION createservice()

DEFINE serv  com.WebService    # A WebService
DEFINE op    com.WebOperation  # Operation of a WebService

Choose a Namespace

XML uses namespaces to group the element and attribute definitions, and to avoid conflicting names. In practice, a namespace must be a unique identifier (URI: Uniform Resource Identifier). If you do not know the unique identifier to use, your company's Web site domain name is guaranteed to be unique (such as "http://www.mycompany.com"); then, append any string.

Examples of valid namespaces for the fictional My Company company: 

Another option (for testing only) is to use the temporary namespace "http://tempuri.org/".

Create the WebService object

Call the constructor method of the WebService class. The parameters are:

  1. Service name
  2. Valid namespace

 This example uses the temporary namespace and creates a Service named "MyCalculator".

LET serv = 
  com.WebService.CreateWebService("MyCalculator", "http://tempuri.org/webservices")

Create the WebOperation object

A WebService object can have multiple operations. The operations can be created in RPC style or Document style by calling the corresponding constructor method of the WebOperation class. The parameters are:

  1. the name of the 4GL function that is executed to process the XML operation
  2. the name you wish to assign to the XML operation
  3. the input record defining the input parameters of the operation (or NULL if there is none)
  4. the output record defining the output parameters of the operation (or NULL if there is none)

To create the operation for the previously defined add function in RPC style:

LET op = com.WebOperation.CreateRPCStyle("add", "Add", add_in, add_out)

To create the operation for the previously defined add function in Document style:

LET op = com.WebOperation.CreateDOCStyle("add", "Add", add_in, add_out)

Mixing RPC style and Document style operations in the same service is not recommended, as it is not WS-I compliant. See Web Services Styles for additional information about styles.

The rest of the code in your application is the same, regardless of the Web Services style that you have chosen.

Publish the operation

Once an operation is defined, it must be associated with its corresponding WebService (the operation must be published). The publishOperation method of the WebService object has the following parameters:

For example, to publish the Add operation of the Calculator service, which was defined as op:

CALL serv.publishOperation(op,NULL)

Step 4: Register the Service

Once the Service and operations are defined and the operations are published, the WebService and WebOperation objects have completed their work.  Registering a service puts the Genero DVM in charge of the execution of all the operations of that service - dispatching the incoming message to the right service, returning the correct output, etc.  The same service may be registered at different locations on the Web.

The WebServiceEngine is a global built-in object that manages the Server part of the Genero DVM.  Use the RegisterService class method of the WebServiceEngine class.  The parameter is:

  1. The name of the WebService object

To register the Calculator service example previous created:

CALL com.WebServiceEngine.RegisterService(serv)

END FUNCTION

Note: If you wanted to create a single GWS Server DVM containing multiple Web Services, you could define additional input and output records and repeat steps 2 through 6 for each Web Service.  In Step 5, a GWS Server DVM is started, containing as many Web Services as you have defined. See Deployment for additional discussion of GWS Services and GWS Servers.

Step 5: Start the GWS Server and process requests

Once you have registered the Web Service(s), you are ready to start the GWS Server and process the incoming SOAP requests.  The GWS Server is located on the same physical machine where the application is being executed (where fglrun is executing). 

This is the MAIN program block of your application.

Define a variable for status

Define a variable to hold the returned status of the request:

MAIN
DEFINE ret INTEGER

Call the function that you created above, which defined and registered the service and its operations:

CALL createservice()

Start the GWS Server

Use the Start class method of the WebServiceEngine class to start the server.

CALL com.WebServiceEngine.Start()

Process the requests

The following example uses the Process Services method of the WebServiceEngine class to process each incoming request, returning an integer representing the status.  The parameter specifies the timeout period in number of seconds for which the method should wait to process a service; the value -1 specifies an infinite waiting time.

WHILE TRUE
  # Process each incoming requests (infinite loop)
  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 
   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
DISPLAY "Server stopped"

END MAIN

Note: The Genero Application Server (GAS) is required to manage your application in a production environment. For deployment, the GWS Server application must be added to the GAS configuration; see "Adding Applications" in the GAS manual. For testing purposes only, the GWS Server can be started in stand-alone mode

Back to the top


Example 2: Writing a GWS Server using Third-Party WSDL (the fglwsdl tool)

To write a Web Service that is compatible with the specification of the input and output records defined by a third-party (for example, a vendor of manufacturing software, or a WSDL specialist in your company) you can use the fglwsdl tool to obtain the WSDL information and generate a part of the Server application, using the steps below. See The fglwsdl Tool for a complete description of the tool and its use. 

Step 1: Get the WSDL description and generate files

This tutorial uses fglwsdl and the Calculator Service defined in Example1 to obtain the WSDL information and generate two corresponding BDL files:

fglwsdl -s -o example1 http://localhost:8090/MyCalculator?WSDL

Note: the MyCalculator GWS Service created in Example1 must be running in order to obtain the WSDL information.

The generated globals file

The globals file example1Service.inc provides the definition of the global input and output records as described in the Step 1 of the Example 1 GWS Server program. The names of the input and output records have been assigned by fglwsdl, in accordance with the Style of the Web Service MyCalculator  (created as RPCStyle in the Example1 program).Do not modify this file.

Input and output records:

# VARIABLE : Add
DEFINE Add RECORD ATTRIBUTE( XMLName="Add", XMLNamespace="http://tempuri.org/webservices" )
  a INTEGER ATTRIBUTE(XMLName="a",XMLNamespace=""),
  b INTEGER ATTRIBUTE(XMLName="b",XMLNamespace="")
END RECORD
# VARIABLE : AddResponse
DEFINE AddResponse RECORD ATTRIBUTE(XMLName="AddResponse", XMLNamespace="http://tempuri.org/webservices" )
  r INTEGER ATTRIBUTE(XMLName="r",XMLNamespace="")
END RECORD

The generated .4gl file

The example1Service.4gl file contains a single function that creates the service, publishes the operation, and registers the Service. The Web Service Style that is created is determined by the style specified in the WSDL information. The functions in this file accomplish the same tasks as Step 3 and Step 4 of Example 1. Do not modify this file.

# example1Service.4gl
# Generated file containing the function Createexample1Service
IMPORT com
GLOBALS "example1Service.inc"

# FUNCTION Createexample1Service
#   RETURNING soapstatus
FUNCTION Createexample1Service()
  DEFINE service      com.WebService
  DEFINE operation    com.WebOperation
  # Set ERROR handler
  WHENEVER ANY ERROR GOTO error
  # Create Web Service
  LET service = com.WebService.CreateWebService("MyCalculator","http://tempuri.org/webservices")
  # Operation: Add
  # Publish Operation : Add
  LET operation = com.WebOperation.CreateRPCStyle("Add","Add",Add,AddResponse)
  CALL service.publishOperation(operation,"")
  # Register Service
  CALL com.WebServiceEngine.RegisterService(service)
  RETURN 0
  # ERROR handler
  LABEL error:
  RETURN STATUS
  # Unset ERROR handler
  WHENEVER ANY ERROR STOP
END FUNCTION

Step 2: Write a BDL function for your Service operation

Using the information from these generated files, the Add operation from Example1 is re-written to have different functionality but to still be compatible with the WSDL description of the operation. This step accomplishes the same thing as Step 2  in Example 1. In this version of the add operation, the sum of the two numbers in the input record is increased by 100.

# my_function.4gl                            -- file containing the function definition

IMPORT com                                   -- import the Web Services library

GLOBALS "example1Service.inc"                -- use the generated globals file

#User Public Functions 
FUNCTION add()                               -- new version of the add function
 LET AddResponse.r = (Add.a + Add.b)+ 100    -- the global input and output records are used
END FUNCTION

Step 3: Create Service/Start Server and process requests

Create your own Main module that calls the function from the generated .4gl file to create the service, and then starts the GWS Server and manages requests as in Step 5 of Example 1.

#example2main.4gl file -- contains the MAIN program block
IMPORT com
GLOBALS "example1Service.inc"
MAIN
  DEFINE create_status INTEGER

  DEFER INTERRUPT

  CALL Createexample1Service()  -- call the function generated in example1Service.4gl 
    RETURNING create_status
  IF create_status <> 0 THEN
    DISPLAY "error"
  ELSE
    # Start the server and manage requests
    CALL ManageService()
  END IF

END MAIN
FUNCTION ManageService()
  DEFINE ret INTEGER
  CALL com.WebServiceEngine.start()
  WHILE TRUE
  # continue as in Step 5 of Example 1
  ...
END FUNCTION

Back to the top


Compiling GWS Server Applications

The library file WSHelper.42m, included in the $FGLDIR/lib directory of the Genero Web Services package, should be linked into every GWS Server application.

If your application uses the fglwsdl tool to generate information, link the .4gl generated file into the application.

Examples:

Compiling the Example1 program:

fglcomp example1.4gl
fgllink -o example1.42r example1.42m WSHelper.42m

Compiling the Example2 program:

fglcomp example2main.4gl my_function.4gl example1Service.4gl
fgllink - o example2.42r example2main.42m my function.42m
   example1Service.42m WSHelper.42m

Back to the top


Testing the GWS Service in stand-alone mode

For testing and development purposes only, the GWS Server application can be executed directly, without using the Genero Application Server (GAS). 

  1. Use the Genero fglrun command to execute the GWS Server application, which must reside on the same machine:

    fglrun <gws application>

    This will start the GWS Server on the port specified by the FGLAPPSERVER environment variable.  If this environment variable is not set for the user, port number 80 is used.  For example, if FGLAPPSERVER is set to 8090, the server will be started on that port.

    Note:  The user must not set the FGLAPPSERVER variable in production environments, since the port number is selected by the Genero Application Server (GAS).

  2. Obtain the WSDL information for your Service and write a test Client application.  If the GWS Server in step 1 was started on your local machine, for example, the command to get the WSDL information would be:
    fglwsdl -o <test-client> http://localhost:8090/<service-name>?WSDL

Back to the top


Configuring the Genero Application Server for the GWS Application

The final step is to configure the Genero Application Server (GAS)  to handle the GWS application. In a production environment, Genero Web Services becomes a part of a global application architecture handled by the application server of the GAS  package. See Deployment, as well as Adding Applications in the GAS manual.

Back to the top


Making the GWS Service available

Once you compile and deploy your GWS Server application (see Deployment), it can be used by others to obtain the WSDL information and write a client application that accesses your Genero Web Service. See Tutorial: Writing a Client Application.

Your company can provide the location of the GWS Server to potential users of your Web Service in various ways; for example:

Back to the top