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.
See also: Choosing a Web Services Style, The fglwsdl Tool, Deployment
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
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.
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
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
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:
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:
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)
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:
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.
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.
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.
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 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 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
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
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
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
For testing and development purposes only, the GWS Server application can be executed directly, without using the Genero Application Server (GAS).
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).
fglwsdl -o <test-client> http://localhost:8090/<service-name>?WSDL
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.
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: