One of the advantages of using the latest version of CICS TG to provide mobile enablement of CICS programs is that we are not limited to using CICS Transaction Server, we can use any of the CICS family of transaction servers that are supported by CICS TG.

In this post I show how to create a RESTful API for customer data using the mobile support introduced in CICS TG V9.1, to invoke transactions on TXSeries. With this API a user can add, retrieve, update or delete a single customer record as well as getting a list of all the customer IDs in the system.

Topology

For development purposes I installed TXSeries 8.1 and CICS TG 9.1 on a Linux on Intel machine and configured an IPIC connection between them. IPIC was used because the CICS program called by the RESTful API has to use a Channel for passing information. Even though the program was written for, and tested in TXSeries the function would work in CICS Transaction Server with no changes.

CICS TG and TXSeries topology

Defining the RESTful API

The API being defined allows for full control of each of the customer records. The first part of the URI, customers/, will allow have the following actions:

Verb Action
GET Return an array of customer URIs
POST Add a new customer

A customer ID can then be added to the URI to interact with that customer record, for example record 0000000001 would be accessed using the URI customers/0000000001. The mapping from HTTP verb to action is shown below:

Verb Action
GET Retrieve the customer record
DELETE Delete the customer record
PUT Update the customer record, or create it if it doesn't exist
POST Not supported for an individual record

Creating the web services bindfile

To build a RESTful API we start with the structure of the customer data that will be passed back and forth and describe this with a JSON schema file

{
    "title": "Retrieve all customers schema",
    "type": "object",
    "properties": {
        "customerid": {
            "type": "string",
            "maxLength": 10
        },
        "firstName": {
            "type": "string",
            "maxLength": 10
        },
        "lastName": {
            "type": "string",
            "maxLength": 20
        },
        "dateofbirth": {
          "type": "string",
          "maxLength": 10
        },
        "houseName": {
          "type": "string",
          "maxLength": 20
        },
        "houseNumber": {
          "type": "string",
          "maxLength": 4
        },
        "postCode": {
          "type": "string",
          "maxLength": 8
        },
        "policyNumbers": {
          "type": "string",
          "maxLength": 3
        },
        "homePhone": {
          "type": "string",
          "maxLength": 20
        },
        "mobilePhone": {
          "type": "string",
          "maxLength": 20
        },
        "eMail": {
          "type": "string",
          "maxLength": 100
        }
    },
    "required": ["customerid", "firstName", "lastName",
      "dateofbirth", "houseName", "houseNumber", "postCode", "policyNumbers",
      "homePhone", "mobilePhone", "eMail"]
}

Once the structure of the data is defined I used the CICS TG Web Services Assistant to generate the language structure header for the back end program and the bind file. The following properties are stored in a properties file:

JSON-SCHEMA-RESTFUL=customer.json
LANG=C
LOGFILE=custrest.log
MAPPING-MODE=JS2LS
LS-RESTFUL=custrest.h
PGMINT=CHANNEL
PGMNAME=CUSTREST
TARGET-CICS-PLATFORM=LinuxI
WSBIND=custrest.wsbind
CHAR-VARYING=NO
DATA-TRUNCATION=ENABLED
CCSID=819

It is worth noting that when creating the bindfile for the RESTful service I only need to specify a single schema and LS parameter as the same data structure is used for input and output. The other key parameters are; TARGET-CICS-PLATFORM which tells the assistant that the CICS program supporting this web service will be running on TXSeries on Linux on Intel, MAPPING-MODE which must be set to JS2LS and LS-RESTFUL which specifies the name of the header file to create, which is then imported into the application.

The properties file is then passed to the ctgassist utility which parses the input and then produces the output header file and web services bind file.

Adding the web service to CICS TG

Having created the bind file the web service definition needs to be added to the CICS TG configuration file. The following section is added to the configuration file:

SECTION WEBSERVICE = CUSTREST
    uri = customers/*
    bindfile = custrest.wsbind
    server = LOCALTX
ENDSECTION

The uri parameter is worth noting that as it ends in an * which allows for the customer ids to be added to the end of the URI as required by our REST API. The rest of the parameters are standard, specifying the location of the bindfile and the name of the server to send the requests to.

In order to have the Gateway daemon process HTTP requests the following configuration is added to the SECTION GATEWAY part of the configuration file:

SUBSECTION HTTP
    port=2080
ENDSUBSECTION

Writing the CICS Program

The CICS program needs to take the following steps during it's execution:

  1. Check which HTTP verb the web service was called with
  2. Perform the required action upon the data

The header file created by the CICS TG web services assistant is included in this program as the contents will be needed when it comes to process data received from and sent to the clients.

Which HTTP verb?

The HTTP verb is passed in a container named DFHHTTPMETHOD so by getting this container with an EXEC CICS GET CONTAINER('DFHHTTPMETHOD') I can compare it against the expected verb names; GET, POST, PUT, DELETE and react accordingly.

Which customer record?

If a customer record number is specified in the URI then it is stored in the DFHWS-URI-RESID container which we can then use to access a specific record.

Working with customer data

Any customer data sent to the program via a PUT or POST request is stored in the DFHWS-DATA container. The contents of this map directly onto the structure created by the assistant, so after performing an EXEC CICS GET CONTAINER the program the information supplied by the client is available to the CICS program. Alternatively when the program needs to send back a customer record, a completed struct can be put into the container for sending back to the client.

Sending a GET request to the /customers/ URI is required to behave differently to all other requests due to the rules of REST APIs. Instead of returning a single customer record the program needs to return an array of all the URIs that are available. As this is a different data structure to the one that is processed by the bindfile transformation the program needs to build the JSON data itself and then have it returned to the client. It does this by putting the response into a container called DFHRESPONSE instead of using the DFHWS-DATA container.

Returning an error

If the CICS program were to end in an error condition then that abend information will be sent back to the client in an error response, but that requires the client to parse the response to find the abend code and parse it. A more RESTful approach is to return a relevant HTTP return code which is more easily understood by the client.

To do this the program can put the return code into a container named DFHHTTPSTATUS with the following format:

HTTP/1.1 nnn text

Where nnn is the return code number and text is the description of the return code. So for the case when a customer id is specified for a record that doesn't exist the program puts the following string into the container:

HTTP/1.1 404 Not Found

Putting it all together

With the CICS program installed into the TXSeries server and everything up and running testing the API is as simple as getting hold of a REST client and sending the relevant HTTP verbs to the URI to either retrieve or update some existing customer records, or create some new records.

Join The Discussion

Your email address will not be published. Required fields are marked *


This site uses Akismet to reduce spam. Learn how your comment data is processed.