IBM Support

Access EGL Applications as a REST Service using Spring Framework

White Papers


Abstract

Tom Baranski
Senior Software Engineer

The EGL language enables applications deployed on various target platforms to be accessible as REST web services. The target platform can be an application server, a local machine, a CICS Transaction Server or a Docker environment.
This paper details the methods in which EGL applications running on a local machine can be accessed as a REST service, using the Spring Framework.

Content

Introduction


Representational State Transfer (REST) web services are services that can be invoked using HTTP. The service provides access to a web resource that is specified in a URI. The service’s operations correspond to HTTP operations, four of which are supported in EGL:

  • GET – for retrieving resources
  • POST – for creating resources
  • PUT – for creating or updating resources
  • DELETE – for deleting resources
EGL provides a common framework for applications to be available as REST web services. This framework has three components:
  1. An EGL service part, which describes the operations made available with REST and the parameters that are passed.
  2. An EGL Deployment Descriptor, which describes how to deploy the EGL REST service to a target platform.
  3. The EGL serviceLib Library, which provides a set of functions for customizing and processing the REST invocation and response.

In EGL, a REST service’s URI is split into two components:
  1. A base URI, which is the leading part of the URI and is usually the address of the REST service
  2. A URI template which is the trailing part of the URI and is usually the name of the service and the parameters of the service.

At run time, the base URI and URI template are concatenated to form the complete URI for invocation. This means that it is also possible to have the full URI in either the baseURI or the URI
Template. For example, if a REST service has a URI as follows:

http://www.example.com/myservice?q={myquery}&numReturnRows={rowCount}

Then the base URI is http://ww.example.com and the URI Template is /myservice?q={myquery}&numReturnRows={rowCount}.
Note: myquery and rowCount are parameters whose values are substituted at run time.
Prerequisites
  • IBM Rational Business Developer (RBD).
  • Knowledge of developing services using EGL and RBD.
  • Knowledge of Spring Framework and OpenAPI/Swagger documentation
     
EGL Annotations For REST Services

Functions in an EGL Service are mapped to a REST operation using the @Rest annotation. There are four types of @Rest annotations that are available, which correspond to the four main REST operations:
  • @GetRest
  • @PutRest
  • @PostRest
  • @DeleteRest
Figure 1 shows an example of an EGL Service that can be used to invoke a REST service. In this example, the service is called service1 and it operates on a web resource described by the record employee.
The value of the parameter empno is used to create the full URI needed to invoke the REST operation GET (EGL getEmployee function).
You can use any number of parameters in the uriTemplate, but only a maximum of one parameter can be used as the resource representation that is sent to and received from the REST service. This parameter is placed in the body of the HTTP request and response. For this example, we are sending and receiving emp as the representation resource.
Figure 1:  An example service with REST API
package services;

record employee type sqlrecord{description = "everything you need to know about an employee",tableNames =[["SAMP.EMPLOYEE"]
            ], fieldsMatchColumns = yes,keyItems = [empno]}

    empno string{column = "EMPNO", maxLen = 6};
    firstnme string{column = "FIRSTNME", sqlVariableLen = yes, maxLen = 12};
    midinit string{column = "MIDINIT", maxLen = 1};
    lastname string{column = "LASTNAME", sqlVariableLen = yes, maxLen = 15};
    workdept string{column = "WORKDEPT", isSqlNullable = yes, maxLen = 3};
    phoneno string{column = "PHONENO", isSqlNullable = yes, maxLen = 4};
    hiredate date{column = "HIREDATE", isSqlNullable = yes, description = "employee's hire date"};
    job string{column = "JOB", isSqlNullable = yes, maxLen = 8};
    edlevel smallInt{column = "EDLEVEL"};
    sex string{column = "SEX", isSqlNullable = yes, maxLen = 1};
    birthdate date{column = "BIRTHDATE", isSqlNullable = yes};
    salary decimal(9, 2){column = "SALARY", isSqlNullable = yes};
    bonus decimal(9, 2){column = "BONUS", isSqlNullable = yes};
    comm decimal(9, 2){column = "COMM", isSqlNullable = yes};
end

service service1{Title = "Employee Service", description = "Operates On The Employee Database", version = "1.0.0"}

    function getEmployee(empno string in) returns(employee){tags =[
                                "employeeRecord", "retrieveEmployee"
                        ], description = "gets an existing employee", @GetRest{uriTemplate = "/employee/{empno}", responseFormat = JSON}}
        
        myemp employee;
        myemp.empno = empno;
        get myemp;
        if(myemp is norecordfound)
            nfresp HttpResponse;
            nfresp.status = 404;
            nfresp.body = "Employee " + empno + " Not Found";
            serviceLib.setRestResponse(nfresp);
        end
        return(myemp);

    end

    function addEmployee(emp employee in){tags =["employeeRecord",
                                "modifyEmployee"
                        ], description = "adds a new employee", @PostRest{uriTemplate = "/newemployee", requestFormat = JSON}}
        
        add emp;
    end

    function updateEmployee(emp employee in){tags =["employeeRecord",
                                "modifyEmployee"
                        ], description = "updates an existing employee", @PutRest{uriTemplate = "/changeemployee", requestFormat = JSON}}
        

        get emp forUpdate;
        if(emp is norecordfound)
            nfresp HttpResponse;
            nfresp.status = 404;
            nfresp.body = "Employee " + emp.empno + " Not Found";
            serviceLib.setRestResponse(nfresp);
        else
            replace emp;
        end

    end

    function deleteEmployee(empno string in){tags =[
                                "employeeRecord", "modifyEmployee"
                        ], description = "deletes an existing employee", @DeleteRest{uriTemplate = "/removeemployee/{empno}"}}
        
        myemp employee;
        myemp.empno = empno;
        get myemp forUpdate;
        if(myemp is norecordfound)
            nfresp HttpResponse;
            nfresp.status = 404;
            nfresp.body = "Employee " + empno + " Not Found";
            serviceLib.setRestResponse(nfresp);
        else
            delete myemp;
        end
    end

end


Each of the @Rest annotations has three properties
 
uriTemplate 

This property allows the user to parameterize the URI. Each parameter in the URI corresponds to a parameter defined in the function. At run time, the values of the parameters are substituted into the URI, which is then appended to the base URI to create the full URI needed to invoke the service.

requestFormat

                This property specifies how the request data is to be formatted when sent to the REST service. There are four formats available in EGL:
  • NONE – the request data is sent as a string
  • XML –  the request data is an EGL record, which is converted at run time to XML
  • JSON – the request data is an EGL record, which is converted at run time to JSON

responseFormat

                 This property specifies the format that the REST service uses to send back data. There are three formats that EGL can understand
  • NONE – the response data is a raw string that is sent to the EGL client as-is
  • XML – the response data is an XML formatted string that is to be converted to an EGL record
  • JSON – the response data is a JSON formatted string that is to be converted to an EGL record

Documenting the REST Service

EGL provides additional annotations for documenting REST Services. This is used for generating the OpenAPI document that defines the REST Service.

This includes: 

Title

This is the title of the OpenAPI document

Description

    A brief description of the service, operation or record

Version

    A user-defined version number of the service

Tags

    Allows the service operations to be grouped by keyword(s) in the OpenAPI document
Refer to Figure 1 for example.
 

Preferences For Documenting the REST Service

Default values for OpenAPI documentation generation are provided in the EGL Services preferences page (refer to Figure 2)
 
Figure 2: Preferences for OpenAPI generation
image 12675
Host Name and Port are used to customize the server URL listed in the OpenAPI document. Note that port 8080 should be specified here for Spring deployment as this is the default port used by Spring.
OpenAPI Version controls the format of the generated OpenAPI document. Default is OpenAPI-3.0, but you can also select OpenAPI-2.0 (also known as Swagger specification).
Specifying Service Deployment In The Deployment Descriptor
To deploy the EGL Service as a REST service, you must specify the Service Deployment in an EGL Deployment Descriptor.
Step 1:  Open the EGL Deployment Descriptor for your project 
Step 2: Go to the Service Deployment tab and click Add . 
Step 3: Select Generate as: REST Service and add the EGL Service you wish to deploy as a REST service and click ‘Finish’
Step 4: If desired, set the URI under REST Web Service Properties. This is the baseURI that the service will be available at. The default value is the service name. The uriTemplate is appended to this to create the invocable URI e.g. http://localhost:8080/service1/employee/{empno}
 
Figure 3: Adding a REST service deployment
image 12676
Figure 4: Rest service deployment
image 12677
Deploying EGL REST Services With Spring
To deploy the EGL Service as a Spring REST service, you must specify the deployment target as a Java project.
Step 1:  Open the build descriptor for your project, and set the property genProject to a Java project (or EGL General project).
Step 2: Open the EGL Deployment Descriptor for your project. 
Step 3: Go to the Overview tab, and set the Deployment Target to be the build descriptor from step 1.
Step 4: Right click on the deployment descriptor, and select Deploy EGL Descriptor.
Step 5: Add the Spring libraries to the Java build path for the target genProject. These libraries are provided for you in the directory: RBD_SHARED_DIR/plugins/com.ibm.etools.egl.rui.deploy.j2ee\lib\eglspring_lib.zip
Figure 5: Deployemnt target for Spring REST deployment
image 12678
The generated Java for the EGL Service will be deployed to the target genProject, along with wrapper artifacts that will allow you to run the EGL Service as a Spring REST application (See Figure 6).
Running EGL REST Services With Spring
To start the EGL REST Service, run the program EGLSpringApplication in the eglrest package (See Figure 6 ). Spring Boot will start its embedded Tomcat server with a default port of 8080. This application can be exported as a runnable jar file, for standalone deployments on local machines or Docker.
Figure 6: Deployable artifacts
image 12679
Invoking The EGL Service
The URL and other information needed to invoke the service is provided in the generated OpenAPI document (refer to service1.json in Figure 6). An example with Swagger Editor is shown in figure 6. Note that the documentation annotations shown in Figure 1 are used to format this presentation. For example, all the operations tagged with ‘employeeRecord’ are grouped together.
The invocation URL is provided by the Servers block (REST API Endpoint), and the paths for each operation are appended to this (refer to Figure 7).
Figure 7: OpenAPI document in Swagger Editor
image 12680
Customizing The Service Response
The system function serviceLib.setRestResponse allows an EGL Java REST Service to set a custom HTTP response depending on the business logic. Otherwise, the default response is 200 OK (along with the service function’s return data) or 500 Internal Server Error (for unhandled exceptions). Refer to Figure 1 for an example of this function.
Syntax:
 
serviceLib.setRestResponse(response HttpResponse in)

The invocation sets the REST HTTP response using the Record part HttpResponse, which is provided for you and has the following fields:
 
body, type STRING?

The response body to be returned from the service;
  • body contains the value in one of three formats (XML, JSON, or NONE), as described in “REST for the developer.” 
    • If body is not specified, then the value of body will be the function response in one of three formats (XML, JSON, or NONE)

headers, type Dictionary

headers contains a set of name-value pairs. Each entry key in the dictionary is the name of an HTTP header that is to be returned from the service, and the related value (a string) is the value of that header.

status, type INT

status contains the HTTP status code for the response. If status is not specified, then the value 200 is used.
Important status codes include 200 (OK) and 404 (Not Found). For a complete list, go to the website of the World Wide Web Consortium (http://www.w3.org/) and search for "HTTP status code.".

statusMessage, type STRING

statusMessage contains the HTTP status message for the response. If statusMessage is not specified, then the value OK is used.

Important status messages include OK (code 200) and Not Found (code 404). For a complete list, go to the website of the World Wide Web Consortium (http://www.w3.org/) and search for "HTTP status code."

[{"Type":"MASTER","Line of Business":{"code":"LOB35","label":"Mainframe SW"},"Business Unit":{"code":"BU058","label":"IBM Infrastructure w\/TPS"},"Product":{"code":"SSMQ79","label":"Rational Business Developer"},"ARM Category":[{"code":"a8m0z0000000BB1AAM","label":"Rational Business Developer"}],"ARM Case Number":"","Platform":[{"code":"PF016","label":"Linux"},{"code":"PF033","label":"Windows"}],"Version":"9.7.0"}]

Document Information

Modified date:
16 February 2023

UID

ibm16587457