19 min read

COBOL systems process an estimated 95% of all ATM transactions and 80% of all in-person financial transactions worldwide. These systems are not relics waiting for retirement. They are mission-critical engines that must now participate in the API...

Chapter 38: Modern COBOL Integrations -- APIs, Web Services, and Microservices

Part VIII - Modern COBOL and System Evolution


COBOL systems process an estimated 95% of all ATM transactions and 80% of all in-person financial transactions worldwide. These systems are not relics waiting for retirement. They are mission-critical engines that must now participate in the API economy, serve mobile applications, feed real-time analytics platforms, and interoperate with cloud-native architectures. This chapter addresses the practical techniques for connecting COBOL programs to the modern world: parsing and generating JSON and XML, calling and exposing RESTful APIs, integrating with message queues, wrapping COBOL logic behind microservice facades, and deploying COBOL workloads in containerized and cloud environments.

The techniques in this chapter build on the CICS foundations covered in Chapters 24 and 25. If you have not yet worked through those chapters, you should review them before tackling the CICS-based integration patterns described here.


38.1 Why Integrate COBOL with Modern Systems

38.1.1 The API Economy

The API economy refers to the set of business models and practices built around exposing software capabilities as network-accessible services. Banks, insurance companies, and government agencies that run COBOL on their mainframes face a clear mandate: the business logic encoded in those COBOL programs must be accessible to mobile apps, partner systems, fintech startups, and internal analytics tools, all of which speak HTTP, JSON, and REST.

Consider a bank that has processed checking account transactions through COBOL batch programs for forty years. The business rules embedded in those programs -- how to calculate fees, how to handle overdraft protection, how to apply regulatory holds -- represent decades of refinement. Rewriting those rules in Java or Python would take years and introduce bugs that the COBOL code resolved long ago. The pragmatic solution is to keep the COBOL logic intact and expose it through modern interfaces.

38.1.2 Digital Transformation Drivers

Several forces drive COBOL modernization through integration rather than replacement:

Mobile Banking: Customers expect real-time account access from their phones. The COBOL programs that manage those accounts must respond to API calls from mobile backends.

Open Banking Regulations: Regulations like PSD2 in Europe and similar frameworks worldwide require banks to expose account data through standardized APIs. The authoritative source of that data is often a COBOL system.

Partner Ecosystems: Fintech companies build products that layer on top of bank infrastructure. These products communicate through APIs, and the bank's COBOL systems must participate.

Internal Modernization: Organizations replacing monolithic architectures with microservices still depend on COBOL for core transaction processing. The microservices must call COBOL programs as backend services.

Cloud Migration: As organizations move workloads to cloud platforms, they need COBOL programs to operate alongside cloud-native services, either running on the cloud themselves or being called from cloud-hosted applications.

38.1.3 Integration Architecture Patterns

There are three fundamental patterns for integrating COBOL with modern systems:

  1. COBOL as Service Provider: The COBOL program is wrapped behind an API layer so that external systems can invoke its business logic over HTTP or through a message queue.

  2. COBOL as Service Consumer: The COBOL program calls external APIs or services to obtain data or trigger actions in other systems.

  3. COBOL as Participant: The COBOL program is one component in a larger workflow orchestrated by middleware such as IBM MQ, Apache Kafka, or an API gateway.

Each of these patterns is explored in detail throughout this chapter.


38.2 COBOL and JSON

38.2.1 JSON in the COBOL World

JSON (JavaScript Object Notation) has become the lingua franca of modern APIs. When a mobile app requests account information from a bank, it expects a JSON response. When a partner system submits a payment request, it sends a JSON payload. COBOL programs that participate in these interactions must be able to parse incoming JSON into COBOL data structures and generate JSON from COBOL data structures for outgoing responses.

IBM Enterprise COBOL V6.1 and later provides native JSON GENERATE and JSON PARSE statements. These are the most efficient and maintainable way to handle JSON in COBOL. Earlier versions of Enterprise COBOL and other compilers require alternative approaches, which we also cover.

IBM-Specific Feature: The JSON GENERATE and JSON PARSE statements described in this section are available in IBM Enterprise COBOL for z/OS V6.1 and later. They are not available in GnuCOBOL or older IBM compilers.

38.2.2 JSON GENERATE

The JSON GENERATE statement converts a COBOL data structure into a JSON string. The compiler maps COBOL group items to JSON objects and COBOL elementary items to JSON name-value pairs.

      ******************************************************************
      * JSON GENERATE EXAMPLE - Account Information Response
      * Requires: IBM Enterprise COBOL V6.1+
      ******************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID. JSONGEN1.

       DATA DIVISION.
       WORKING-STORAGE SECTION.

      * Source COBOL data structure
       01  WS-ACCOUNT-INFO.
           05  accountNumber         PIC 9(10)
                                     VALUE 1000045678.
           05  holderName            PIC X(30)
                                     VALUE "MARIA GARCIA".
           05  accountType           PIC X(10)
                                     VALUE "SAVINGS".
           05  currentBalance        PIC S9(11)V99
                                     VALUE 15234.50.
           05  availableBalance      PIC S9(11)V99
                                     VALUE 15234.50.
           05  currencyCode          PIC X(3)
                                     VALUE "USD".
           05  accountStatus         PIC X(8)
                                     VALUE "ACTIVE".
           05  dateOpened            PIC X(10)
                                     VALUE "2019-03-15".
           05  overdraftLimit        PIC S9(9)V99
                                     VALUE 0.

      * Target JSON buffer
       01  WS-JSON-OUTPUT            PIC X(2000).
       01  WS-JSON-LENGTH            PIC 9(5).
       01  WS-JSON-STATUS            PIC 9(2).

       PROCEDURE DIVISION.
       MAIN-LOGIC.
           JSON GENERATE WS-JSON-OUTPUT
               FROM WS-ACCOUNT-INFO
               COUNT IN WS-JSON-LENGTH
               ON EXCEPTION
                   MOVE 1 TO WS-JSON-STATUS
                   DISPLAY "JSON GENERATE failed"
               NOT ON EXCEPTION
                   MOVE 0 TO WS-JSON-STATUS
                   DISPLAY "Generated JSON ("
                       WS-JSON-LENGTH " bytes):"
                   DISPLAY WS-JSON-OUTPUT(1:WS-JSON-LENGTH)
           END-JSON

           STOP RUN.

The generated JSON output would look like this:

{
  "accountNumber": 1000045678,
  "holderName": "MARIA GARCIA",
  "accountType": "SAVINGS",
  "currentBalance": 15234.50,
  "availableBalance": 15234.50,
  "currencyCode": "USD",
  "accountStatus": "ACTIVE",
  "dateOpened": "2019-03-15",
  "overdraftLimit": 0
}

Key rules for JSON GENERATE:

  • The JSON field names are derived from the COBOL data-name identifiers. For this reason, use mixed-case or camelCase names in your COBOL data definitions when you know they will be used for JSON generation.
  • FILLER items are excluded from the generated JSON.
  • The SUPPRESS phrase allows you to omit fields with zero or space values.
  • The NAME phrase allows you to override the default JSON field names.
      * Using SUPPRESS and NAME phrases
           JSON GENERATE WS-JSON-OUTPUT
               FROM WS-ACCOUNT-INFO
               COUNT IN WS-JSON-LENGTH
               SUPPRESS overdraftLimit
                   WHEN overdraftLimit = ZERO
               NAME OF holderName IS "customerName"
                      accountNumber IS "acctNo"
           END-JSON

38.2.3 JSON PARSE

The JSON PARSE statement converts a JSON string into a COBOL data structure. The parser matches JSON field names to COBOL data names and moves the values accordingly.

      ******************************************************************
      * JSON PARSE EXAMPLE - Incoming Transfer Request
      * Requires: IBM Enterprise COBOL V6.1+
      ******************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID. JSONPRS1.

       DATA DIVISION.
       WORKING-STORAGE SECTION.

      * Incoming JSON string (would normally come from a network
      * request via CICS or MQ)
       01  WS-JSON-INPUT             PIC X(1000).
       01  WS-JSON-INPUT-LEN         PIC 9(5).

      * Target COBOL data structure
       01  WS-TRANSFER-REQUEST.
           05  sourceAccount         PIC 9(10).
           05  targetAccount         PIC 9(10).
           05  transferAmount        PIC S9(11)V99.
           05  currencyCode          PIC X(3).
           05  transferReference     PIC X(20).
           05  transferDate          PIC X(10).
           05  transferMemo          PIC X(50).

       01  WS-PARSE-STATUS           PIC 9(2).
       01  WS-DISPLAY-AMT            PIC -(11)9.99.

       PROCEDURE DIVISION.
       MAIN-LOGIC.
      *    Simulate receiving JSON input
           STRING
               '{"sourceAccount":1000045678,'
               '"targetAccount":2000098765,'
               '"transferAmount":2500.00,'
               '"currencyCode":"USD",'
               '"transferReference":"TRF-2025-001234",'
               '"transferDate":"2025-01-15",'
               '"transferMemo":"Monthly rent payment"}'
               DELIMITED BY SIZE
               INTO WS-JSON-INPUT
               WITH POINTER WS-JSON-INPUT-LEN
           END-STRING
           SUBTRACT 1 FROM WS-JSON-INPUT-LEN

           INITIALIZE WS-TRANSFER-REQUEST

           JSON PARSE WS-JSON-INPUT
               INTO WS-TRANSFER-REQUEST
               WITH DETAIL
               ON EXCEPTION
                   MOVE 1 TO WS-PARSE-STATUS
                   DISPLAY "JSON PARSE failed"
               NOT ON EXCEPTION
                   MOVE 0 TO WS-PARSE-STATUS
           END-JSON

           IF WS-PARSE-STATUS = 0
               DISPLAY "Transfer Request Parsed:"
               DISPLAY "  From Account: " sourceAccount
               DISPLAY "  To Account:   " targetAccount
               MOVE transferAmount TO WS-DISPLAY-AMT
               DISPLAY "  Amount:       $" WS-DISPLAY-AMT
               DISPLAY "  Currency:     " currencyCode
               DISPLAY "  Reference:    " transferReference
               DISPLAY "  Date:         " transferDate
               DISPLAY "  Memo:         " transferMemo
           END-IF

           STOP RUN.

38.2.4 JSON Handling Without Native Support

For compilers that lack JSON GENERATE and JSON PARSE (including GnuCOBOL and older IBM Enterprise COBOL versions), you must handle JSON through string manipulation or external libraries.

GnuCOBOL Alternative - Manual JSON Construction:

      ******************************************************************
      * MANUAL JSON GENERATION FOR GNUCOBOL
      * Builds JSON strings using STRING verb and helper paragraphs.
      * Compatible with: GnuCOBOL 2.x+, any COBOL-85 compiler
      ******************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID. JSONMANL.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-JSON-BUFFER            PIC X(4000) VALUE SPACES.
       01  WS-JSON-PTR               PIC 9(5) VALUE 1.
       01  WS-TEMP-NUM               PIC -(11)9.99.
       01  WS-TRIMMED-VALUE          PIC X(100).
       01  WS-TRIM-LEN               PIC 9(3).

      * Account data
       01  WS-ACCT-NUMBER            PIC 9(10)  VALUE 1000045678.
       01  WS-ACCT-HOLDER            PIC X(30)  VALUE
                                     "MARIA GARCIA".
       01  WS-ACCT-BALANCE           PIC S9(11)V99 VALUE 15234.50.
       01  WS-ACCT-STATUS            PIC X(8)   VALUE "ACTIVE".

       PROCEDURE DIVISION.
       MAIN-LOGIC.
           MOVE 1 TO WS-JSON-PTR

           PERFORM APPEND-OPEN-BRACE

           PERFORM APPEND-KEY-VALUE-NUM
               USING "accountNumber" WS-ACCT-NUMBER
           PERFORM APPEND-COMMA

           MOVE WS-ACCT-HOLDER TO WS-TRIMMED-VALUE
           PERFORM TRIM-TRAILING-SPACES
           PERFORM APPEND-KEY-VALUE-STR
               USING "holderName" WS-TRIMMED-VALUE

           PERFORM APPEND-COMMA

           MOVE WS-ACCT-BALANCE TO WS-TEMP-NUM
           PERFORM APPEND-KEY-VALUE-RAW
               USING "currentBalance" WS-TEMP-NUM

           PERFORM APPEND-COMMA

           MOVE WS-ACCT-STATUS TO WS-TRIMMED-VALUE
           PERFORM TRIM-TRAILING-SPACES
           PERFORM APPEND-KEY-VALUE-STR
               USING "accountStatus" WS-TRIMMED-VALUE

           PERFORM APPEND-CLOSE-BRACE

           DISPLAY WS-JSON-BUFFER(1:WS-JSON-PTR - 1)
           STOP RUN.

       APPEND-OPEN-BRACE.
           STRING "{" DELIMITED BY SIZE
               INTO WS-JSON-BUFFER
               WITH POINTER WS-JSON-PTR
           END-STRING.

       APPEND-CLOSE-BRACE.
           STRING "}" DELIMITED BY SIZE
               INTO WS-JSON-BUFFER
               WITH POINTER WS-JSON-PTR
           END-STRING.

       APPEND-COMMA.
           STRING "," DELIMITED BY SIZE
               INTO WS-JSON-BUFFER
               WITH POINTER WS-JSON-PTR
           END-STRING.

       APPEND-KEY-VALUE-STR USING
           L-KEY PIC X(30) L-VAL PIC X(100).
           STRING '"' DELIMITED BY SIZE
               L-KEY DELIMITED BY SPACES
               '":"' DELIMITED BY SIZE
               L-VAL DELIMITED BY "  "
               '"' DELIMITED BY SIZE
               INTO WS-JSON-BUFFER
               WITH POINTER WS-JSON-PTR
           END-STRING.

       APPEND-KEY-VALUE-NUM USING
           L-KEY PIC X(30) L-VAL PIC 9(10).
           STRING '"' DELIMITED BY SIZE
               L-KEY DELIMITED BY SPACES
               '":' DELIMITED BY SIZE
               L-VAL DELIMITED BY SIZE
               INTO WS-JSON-BUFFER
               WITH POINTER WS-JSON-PTR
           END-STRING.

       APPEND-KEY-VALUE-RAW USING
           L-KEY PIC X(30) L-VAL PIC X(20).
           STRING '"' DELIMITED BY SIZE
               L-KEY DELIMITED BY SPACES
               '":' DELIMITED BY SIZE
               L-VAL DELIMITED BY SPACES
               INTO WS-JSON-BUFFER
               WITH POINTER WS-JSON-PTR
           END-STRING.

       TRIM-TRAILING-SPACES.
           MOVE FUNCTION LENGTH(
               FUNCTION TRIM(WS-TRIMMED-VALUE TRAILING))
               TO WS-TRIM-LEN.

GnuCOBOL Alternative - Using C JSON Libraries:

GnuCOBOL can call C functions directly. This allows you to use established C JSON libraries like cJSON or Jansson:

      ******************************************************************
      * GNUCOBOL JSON VIA C LIBRARY (cJSON)
      * Calls cJSON library functions through GnuCOBOL's C interface.
      * Compatible with: GnuCOBOL 2.x+ with cJSON library installed
      * Compile: cobc -x jsonc.cob -lcjson
      ******************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID. JSONCLIB.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-JSON-ROOT              USAGE POINTER.
       01  WS-JSON-ITEM              USAGE POINTER.
       01  WS-JSON-STRING            USAGE POINTER.
       01  WS-JSON-OUTPUT            PIC X(2000).
       01  WS-ACCT-NUM               PIC 9(10) VALUE 1000045678.
       01  WS-BALANCE-INT            PIC S9(9) COMP-5.
       01  WS-HOLDER                 PIC X(30)
                                     VALUE "MARIA GARCIA" & X"00".

       PROCEDURE DIVISION.
       MAIN-LOGIC.
      *    Create JSON object using cJSON C library calls
           CALL "cJSON_CreateObject"
               RETURNING WS-JSON-ROOT
           END-CALL

      *    Add string field
           CALL "cJSON_AddStringToObject"
               USING BY VALUE WS-JSON-ROOT
                     BY REFERENCE "holderName" & X"00"
                     BY REFERENCE WS-HOLDER
           END-CALL

      *    Add number field
           MOVE 15234 TO WS-BALANCE-INT
           CALL "cJSON_AddNumberToObject"
               USING BY VALUE WS-JSON-ROOT
                     BY REFERENCE "balance" & X"00"
                     BY VALUE WS-BALANCE-INT
           END-CALL

      *    Print the JSON
           CALL "cJSON_Print"
               USING BY VALUE WS-JSON-ROOT
               RETURNING WS-JSON-STRING
           END-CALL

      *    Clean up
           CALL "cJSON_Delete"
               USING BY VALUE WS-JSON-ROOT
           END-CALL

           STOP RUN.

38.3 COBOL and XML

38.3.1 XML Processing in COBOL

XML remains important in enterprise integrations, particularly in SOAP web services, financial messaging standards (ISO 20022, SWIFT MX), and EDI transformations. IBM Enterprise COBOL has provided XML GENERATE and XML PARSE since V3.4, making XML support available on a wider range of compiler versions than JSON.

IBM-Specific Feature: XML GENERATE and XML PARSE are available in IBM Enterprise COBOL V3.4 and later. GnuCOBOL does not provide native XML support but can use external C libraries such as libxml2.

38.3.2 XML GENERATE

      ******************************************************************
      * XML GENERATE EXAMPLE - Account Statement
      * Requires: IBM Enterprise COBOL V3.4+
      ******************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID. XMLGEN1.

       DATA DIVISION.
       WORKING-STORAGE SECTION.

       01  WS-ACCOUNT-STATEMENT.
           05  AccountStatement.
               10  Header.
                   15  StatementDate    PIC X(10)
                                        VALUE "2025-01-31".
                   15  AccountNumber    PIC 9(10)
                                        VALUE 1000045678.
                   15  AccountHolder    PIC X(30)
                                        VALUE "MARIA GARCIA".
                   15  StatementPeriod.
                       20  PeriodStart  PIC X(10)
                                        VALUE "2025-01-01".
                       20  PeriodEnd    PIC X(10)
                                        VALUE "2025-01-31".
               10  Summary.
                   15  OpeningBalance   PIC S9(11)V99
                                        VALUE 12500.00.
                   15  TotalCredits     PIC S9(11)V99
                                        VALUE 5234.50.
                   15  TotalDebits      PIC S9(11)V99
                                        VALUE 2500.00.
                   15  ClosingBalance   PIC S9(11)V99
                                        VALUE 15234.50.
                   15  InterestEarned   PIC S9(7)V99
                                        VALUE 26.08.
               10  Transactions.
                   15  TransactionEntry OCCURS 3 TIMES.
                       20  TxnDate     PIC X(10).
                       20  TxnType     PIC X(10).
                       20  TxnDesc     PIC X(30).
                       20  TxnAmount   PIC S9(11)V99.
                       20  TxnBalance  PIC S9(11)V99.

       01  WS-XML-OUTPUT              PIC X(8000).
       01  WS-XML-LENGTH              PIC 9(5).
       01  WS-XML-STATUS              PIC 9(2).

       PROCEDURE DIVISION.
       MAIN-LOGIC.
      *    Populate transaction entries
           MOVE "2025-01-05" TO TxnDate(1)
           MOVE "CREDIT"     TO TxnType(1)
           MOVE "PAYROLL DEPOSIT"
                              TO TxnDesc(1)
           MOVE 3500.00       TO TxnAmount(1)
           MOVE 16000.00      TO TxnBalance(1)

           MOVE "2025-01-12" TO TxnDate(2)
           MOVE "DEBIT"      TO TxnType(2)
           MOVE "RENT PAYMENT"
                              TO TxnDesc(2)
           MOVE -1800.00     TO TxnAmount(2)
           MOVE 14200.00     TO TxnBalance(2)

           MOVE "2025-01-20" TO TxnDate(3)
           MOVE "CREDIT"     TO TxnType(3)
           MOVE "TRANSFER FROM SAVINGS"
                              TO TxnDesc(3)
           MOVE 1734.50      TO TxnAmount(3)
           MOVE 15934.50     TO TxnBalance(3)

           XML GENERATE WS-XML-OUTPUT
               FROM AccountStatement
               COUNT IN WS-XML-LENGTH
               WITH XML-DECLARATION
               WITH ATTRIBUTES
               ON EXCEPTION
                   MOVE 1 TO WS-XML-STATUS
                   DISPLAY "XML GENERATE failed"
               NOT ON EXCEPTION
                   MOVE 0 TO WS-XML-STATUS
                   DISPLAY WS-XML-OUTPUT(1:WS-XML-LENGTH)
           END-XML

           STOP RUN.

38.3.3 XML PARSE

The XML PARSE statement is an event-driven (SAX-style) parser. Unlike JSON PARSE, which maps the entire document into a data structure at once, XML PARSE invokes a processing procedure for each XML event (start of element, end of element, character content, etc.).

      ******************************************************************
      * XML PARSE EXAMPLE - Incoming Payment Instruction
      * Requires: IBM Enterprise COBOL V3.4+
      ******************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID. XMLPRS1.

       DATA DIVISION.
       WORKING-STORAGE SECTION.

       01  WS-XML-INPUT              PIC X(2000).

       01  WS-PAYMENT-INSTRUCTION.
           05  WS-PMT-SOURCE-ACCT   PIC 9(10).
           05  WS-PMT-TARGET-ACCT   PIC 9(10).
           05  WS-PMT-AMOUNT        PIC S9(11)V99.
           05  WS-PMT-CURRENCY      PIC X(3).
           05  WS-PMT-REF           PIC X(20).
           05  WS-PMT-DATE          PIC X(10).

       01  WS-CURRENT-ELEMENT        PIC X(30).
       01  WS-PARSE-STATUS           PIC 9(2).
       01  WS-DISPLAY-AMT            PIC -(11)9.99.

       PROCEDURE DIVISION.
       MAIN-LOGIC.
      *    Simulate incoming XML
           STRING
               '<PaymentInstruction>'
               '<SourceAccount>1000045678</SourceAccount>'
               '<TargetAccount>2000098765</TargetAccount>'
               '<Amount>2500.00</Amount>'
               '<Currency>USD</Currency>'
               '<Reference>PMT-2025-005678</Reference>'
               '<ValueDate>2025-01-20</ValueDate>'
               '</PaymentInstruction>'
               DELIMITED BY SIZE
               INTO WS-XML-INPUT
           END-STRING

           INITIALIZE WS-PAYMENT-INSTRUCTION
           MOVE SPACES TO WS-CURRENT-ELEMENT

           XML PARSE WS-XML-INPUT
               PROCESSING PROCEDURE XML-HANDLER
               ON EXCEPTION
                   MOVE 1 TO WS-PARSE-STATUS
                   DISPLAY "XML PARSE failed"
               NOT ON EXCEPTION
                   MOVE 0 TO WS-PARSE-STATUS
           END-XML

           IF WS-PARSE-STATUS = 0
               DISPLAY "Payment Instruction Parsed:"
               DISPLAY "  Source: " WS-PMT-SOURCE-ACCT
               DISPLAY "  Target: " WS-PMT-TARGET-ACCT
               MOVE WS-PMT-AMOUNT TO WS-DISPLAY-AMT
               DISPLAY "  Amount: $" WS-DISPLAY-AMT
               DISPLAY "  Currency: " WS-PMT-CURRENCY
               DISPLAY "  Reference: " WS-PMT-REF
               DISPLAY "  Date: " WS-PMT-DATE
           END-IF

           STOP RUN.

       XML-HANDLER.
           EVALUATE XML-EVENT
               WHEN "START-OF-ELEMENT"
                   MOVE XML-TEXT TO WS-CURRENT-ELEMENT
               WHEN "CONTENT-CHARACTERS"
                   EVALUATE WS-CURRENT-ELEMENT
                       WHEN "SourceAccount"
                           MOVE XML-TEXT
                               TO WS-PMT-SOURCE-ACCT
                       WHEN "TargetAccount"
                           MOVE XML-TEXT
                               TO WS-PMT-TARGET-ACCT
                       WHEN "Amount"
                           COMPUTE WS-PMT-AMOUNT =
                               FUNCTION NUMVAL(XML-TEXT)
                       WHEN "Currency"
                           MOVE XML-TEXT TO WS-PMT-CURRENCY
                       WHEN "Reference"
                           MOVE XML-TEXT TO WS-PMT-REF
                       WHEN "ValueDate"
                           MOVE XML-TEXT TO WS-PMT-DATE
                   END-EVALUATE
               WHEN "END-OF-ELEMENT"
                   MOVE SPACES TO WS-CURRENT-ELEMENT
           END-EVALUATE.

38.4 RESTful API Integration via CICS Web Services

38.4.1 COBOL as a REST API Consumer

CICS TS V5.2 and later provides the EXEC CICS WEB OPEN, WEB CONVERSE, and WEB CLOSE commands that allow COBOL programs running in CICS to call external REST APIs. This enables mainframe COBOL to consume cloud services, third-party APIs, and internal microservices.

IBM-Specific Feature: The EXEC CICS WEB commands require CICS Transaction Server V5.2 or later. These are not available outside the CICS environment or in GnuCOBOL.

      ******************************************************************
      * COBOL REST API CONSUMER VIA CICS
      * Calls an external exchange rate API to get current rates
      * for currency conversion in a banking transaction.
      * Requires: IBM CICS TS V5.2+, Enterprise COBOL V5+
      ******************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID. RESTCALL.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-SESSION-TOKEN          PIC X(8).
       01  WS-HOST                   PIC X(50)
                                     VALUE "api.exchangerates.internal".
       01  WS-HOST-LEN               PIC S9(8) COMP VALUE 31.
       01  WS-PATH                   PIC X(100)
                                     VALUE "/v1/rates/USD/EUR".
       01  WS-PATH-LEN               PIC S9(8) COMP VALUE 17.
       01  WS-PORT                   PIC S9(8) COMP VALUE 443.

       01  WS-HTTP-METHOD            PIC X(4) VALUE "GET ".
       01  WS-MEDIA-TYPE             PIC X(20)
                                     VALUE "application/json".
       01  WS-STATUS-CODE            PIC S9(8) COMP.
       01  WS-STATUS-TEXT            PIC X(50).
       01  WS-STATUS-LEN             PIC S9(8) COMP.

       01  WS-RESPONSE-BODY          PIC X(4000).
       01  WS-RESPONSE-LEN           PIC S9(8) COMP VALUE 4000.

       01  WS-RATE-RESPONSE.
           05  baseCurrency          PIC X(3).
           05  targetCurrency        PIC X(3).
           05  exchangeRate          PIC 9V9(6).
           05  rateDate              PIC X(10).

       01  WS-RESP                   PIC S9(8) COMP.
       01  WS-RESP2                  PIC S9(8) COMP.

       PROCEDURE DIVISION.
       MAIN-LOGIC.
           PERFORM OPEN-CONNECTION
           PERFORM SEND-REQUEST
           PERFORM PARSE-RESPONSE
           PERFORM CLOSE-CONNECTION
           EXEC CICS RETURN END-EXEC.

       OPEN-CONNECTION.
           EXEC CICS WEB OPEN
               HOST(WS-HOST)
               HOSTLENGTH(WS-HOST-LEN)
               PORTNUMBER(WS-PORT)
               SCHEME(HTTPS)
               SESSTOKEN(WS-SESSION-TOKEN)
               RESP(WS-RESP)
               RESP2(WS-RESP2)
           END-EXEC

           IF WS-RESP NOT = DFHRESP(NORMAL)
               DISPLAY "WEB OPEN failed: " WS-RESP " " WS-RESP2
               EXEC CICS RETURN END-EXEC
           END-IF.

       SEND-REQUEST.
           EXEC CICS WEB CONVERSE
               SESSTOKEN(WS-SESSION-TOKEN)
               PATH(WS-PATH)
               PATHLENGTH(WS-PATH-LEN)
               METHOD(WS-HTTP-METHOD)
               MEDIATYPE(WS-MEDIA-TYPE)
               INTO(WS-RESPONSE-BODY)
               TOLENGTH(WS-RESPONSE-LEN)
               STATUSCODE(WS-STATUS-CODE)
               STATUSTEXT(WS-STATUS-TEXT)
               STATUSLEN(WS-STATUS-LEN)
               RESP(WS-RESP)
               RESP2(WS-RESP2)
           END-EXEC

           IF WS-RESP NOT = DFHRESP(NORMAL)
               DISPLAY "WEB CONVERSE failed: " WS-RESP
               EXEC CICS RETURN END-EXEC
           END-IF

           IF WS-STATUS-CODE NOT = 200
               DISPLAY "HTTP Error: " WS-STATUS-CODE
               DISPLAY "Response: " WS-STATUS-TEXT
           END-IF.

       PARSE-RESPONSE.
           IF WS-STATUS-CODE = 200
               JSON PARSE WS-RESPONSE-BODY
                   INTO WS-RATE-RESPONSE
                   ON EXCEPTION
                       DISPLAY "Failed to parse rate response"
                   NOT ON EXCEPTION
                       DISPLAY "Exchange Rate Retrieved:"
                       DISPLAY "  " baseCurrency "/" targetCurrency
                               ": " exchangeRate
                       DISPLAY "  Rate Date: " rateDate
               END-JSON
           END-IF.

       CLOSE-CONNECTION.
           EXEC CICS WEB CLOSE
               SESSTOKEN(WS-SESSION-TOKEN)
               RESP(WS-RESP)
           END-EXEC.

38.4.2 COBOL as a REST API Provider via CICS

CICS can also expose COBOL programs as REST service providers. When an HTTP request arrives at CICS, it can route the request to a COBOL program that processes it and returns an HTTP response.

      ******************************************************************
      * COBOL REST API PROVIDER VIA CICS
      * Handles incoming HTTP GET requests for account balance
      * inquiries. Returns JSON response.
      * Requires: IBM CICS TS V5.2+, Enterprise COBOL V6.1+
      ******************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID. ACCTAPI.

       DATA DIVISION.
       WORKING-STORAGE SECTION.

      * HTTP request data
       01  WS-REQUEST-PATH           PIC X(200).
       01  WS-PATH-LEN               PIC S9(8) COMP.
       01  WS-HTTP-METHOD            PIC X(10).
       01  WS-METHOD-LEN             PIC S9(8) COMP VALUE 10.
       01  WS-REQUEST-BODY           PIC X(4000).
       01  WS-REQUEST-LEN            PIC S9(8) COMP VALUE 4000.

      * Extracted path parameter (account number)
       01  WS-ACCT-NUM-PARAM         PIC X(10).

      * Account data (would normally come from DB2 or VSAM)
       01  WS-ACCOUNT-DATA.
           05  accountNumber         PIC 9(10).
           05  holderName            PIC X(30).
           05  accountType           PIC X(10).
           05  currentBalance        PIC S9(11)V99.
           05  availableBalance      PIC S9(11)V99.
           05  currencyCode          PIC X(3).
           05  accountStatus         PIC X(8).

      * HTTP response data
       01  WS-RESPONSE-BODY          PIC X(4000).
       01  WS-RESPONSE-LEN           PIC S9(8) COMP.
       01  WS-STATUS-CODE            PIC S9(8) COMP.
       01  WS-STATUS-TEXT            PIC X(50).
       01  WS-STATUS-TEXT-LEN        PIC S9(8) COMP.
       01  WS-CONTENT-TYPE           PIC X(30)
                                     VALUE "application/json".

      * Error response
       01  WS-ERROR-RESPONSE.
           05  errorCode             PIC X(10).
           05  errorMessage          PIC X(80).

       01  WS-RESP                   PIC S9(8) COMP.
       01  WS-FOUND-FLAG             PIC 9 VALUE 0.
           88 ACCOUNT-FOUND          VALUE 1.
           88 ACCOUNT-NOT-FOUND      VALUE 0.

       PROCEDURE DIVISION.
       MAIN-LOGIC.
           PERFORM EXTRACT-REQUEST-INFO
           PERFORM LOOKUP-ACCOUNT
           PERFORM SEND-RESPONSE
           EXEC CICS RETURN END-EXEC.

       EXTRACT-REQUEST-INFO.
      *    Get the HTTP method
           EXEC CICS WEB READ
               HTTPMETHOD(WS-HTTP-METHOD)
               METHODLENGTH(WS-METHOD-LEN)
               RESP(WS-RESP)
           END-EXEC

      *    Get the request path to extract account number
           EXEC CICS WEB READ
               PATH(WS-REQUEST-PATH)
               PATHLENGTH(WS-PATH-LEN)
               RESP(WS-RESP)
           END-EXEC

      *    Extract account number from path
      *    Expected format: /api/v1/accounts/1000045678
           MOVE WS-REQUEST-PATH(23:10)
               TO WS-ACCT-NUM-PARAM.

       LOOKUP-ACCOUNT.
      *    In production, this would query DB2 or VSAM
      *    Simulated lookup for demonstration
           IF WS-ACCT-NUM-PARAM = "1000045678"
               MOVE 1000045678 TO accountNumber
               MOVE "MARIA GARCIA" TO holderName
               MOVE "SAVINGS" TO accountType
               MOVE 15234.50 TO currentBalance
               MOVE 15234.50 TO availableBalance
               MOVE "USD" TO currencyCode
               MOVE "ACTIVE" TO accountStatus
               SET ACCOUNT-FOUND TO TRUE
           ELSE
               SET ACCOUNT-NOT-FOUND TO TRUE
           END-IF.

       SEND-RESPONSE.
           IF ACCOUNT-FOUND
               JSON GENERATE WS-RESPONSE-BODY
                   FROM WS-ACCOUNT-DATA
                   COUNT IN WS-RESPONSE-LEN
               END-JSON
               MOVE 200 TO WS-STATUS-CODE
               MOVE "OK" TO WS-STATUS-TEXT
               MOVE 2 TO WS-STATUS-TEXT-LEN
           ELSE
               MOVE "NOT_FOUND" TO errorCode
               MOVE "Account not found" TO errorMessage
               JSON GENERATE WS-RESPONSE-BODY
                   FROM WS-ERROR-RESPONSE
                   COUNT IN WS-RESPONSE-LEN
               END-JSON
               MOVE 404 TO WS-STATUS-CODE
               MOVE "Not Found" TO WS-STATUS-TEXT
               MOVE 9 TO WS-STATUS-TEXT-LEN
           END-IF

           EXEC CICS WEB SEND
               FROM(WS-RESPONSE-BODY)
               FROMLENGTH(WS-RESPONSE-LEN)
               MEDIATYPE(WS-CONTENT-TYPE)
               STATUSCODE(WS-STATUS-CODE)
               STATUSTEXT(WS-STATUS-TEXT)
               STATUSLEN(WS-STATUS-TEXT-LEN)
               RESP(WS-RESP)
           END-EXEC.

38.5 SOAP Web Services

38.5.1 COBOL and SOAP via CICS

SOAP (Simple Object Access Protocol) web services use XML-based messages exchanged over HTTP. While REST has become more popular for new APIs, SOAP remains prevalent in enterprise and financial services integrations, particularly in established B2B connections and regulatory reporting systems.

IBM-Specific Feature: CICS provides built-in support for SOAP web services through the CICS Web Services Assistant. This tooling generates COBOL data structures (copybooks) from WSDL definitions and configures CICS pipelines to handle SOAP message processing.

38.5.2 CICS as a SOAP Service Provider

When CICS acts as a SOAP service provider, the process works as follows:

  1. Define the service interface using WSDL (Web Services Description Language).
  2. Use the CICS Web Services Assistant tool (DFHLS2WS) to generate a COBOL copybook from the WSDL.
  3. Write a COBOL program that uses the generated copybook to receive the input message and produce the output message.
  4. Deploy the service by defining a CICS PIPELINE and WEBSERVICE resource.
//*----------------------------------------------------------*
//* JCL TO RUN CICS WEB SERVICES ASSISTANT (DFHLS2WS)
//* Generates COBOL copybook from WSDL definition
//* IBM-Specific: Requires CICS TS V3.1+
//*----------------------------------------------------------*
//WSASSIST EXEC PGM=DFHLS2WS,
//         PARM='LOGFILE=YES'
//STEPLIB  DD DSN=CICSTS56.CICS.SDFHLOAD,DISP=SHR
//SYSPRINT DD SYSOUT=*
//SYSIN    DD *
  PDSLIB=ACCTDEV.COBOL.COPYLIB
  REQMEM=ACCTREQ
  RESPMEM=ACCTRESP
  LOGFILE=YES
  WSDL=ACCTDEV.WSDL.LIBRARY(BALANCESVC)
  LANG=COBOL
  MAPPING-LEVEL=3.0
  PGMNAME=BALSVC
  TRANSACTION=BSVC
  URI=/ws/balance
/*

The generated copybook (ACCTREQ) might look like this:

      * Generated by DFHLS2WS from BalanceInquiry WSDL
      * Do not modify manually
       01  DFHWS-BALANCE-REQUEST.
           05  DFHWS-ACCOUNT-NUMBER  PIC 9(10).
           05  DFHWS-INQUIRY-TYPE    PIC X(10).
           05  DFHWS-AS-OF-DATE      PIC X(10).

And the response copybook (ACCTRESP):

      * Generated by DFHLS2WS from BalanceInquiry WSDL
       01  DFHWS-BALANCE-RESPONSE.
           05  DFHWS-ACCOUNT-NUMBER  PIC 9(10).
           05  DFHWS-HOLDER-NAME     PIC X(30).
           05  DFHWS-BALANCE-AMT     PIC S9(11)V99.
           05  DFHWS-AVAILABLE-AMT   PIC S9(11)V99.
           05  DFHWS-CURRENCY        PIC X(3).
           05  DFHWS-STATUS-CODE     PIC X(4).
           05  DFHWS-STATUS-MSG      PIC X(50).

The COBOL service program:

      ******************************************************************
      * SOAP WEB SERVICE - Balance Inquiry
      * CICS handles SOAP envelope processing; this program only
      * deals with the business data payload.
      * Requires: IBM CICS TS V3.1+, Enterprise COBOL V4+
      ******************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID. BALSVC.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-DB2-SQLCODE           PIC S9(4) COMP.
       01  WS-RESP                  PIC S9(8) COMP.

       COPY ACCTREQ.
       COPY ACCTRESP.

      * Container names for CICS channel-based communication
       01  WS-INPUT-CONTAINER       PIC X(16)
                                    VALUE "DFHWS-DATA".
       01  WS-OUTPUT-CONTAINER      PIC X(16)
                                    VALUE "DFHWS-DATA".
       01  WS-CHANNEL-NAME          PIC X(16)
                                    VALUE "DFHWS-CHANNEL".
       01  WS-DATA-LENGTH           PIC S9(8) COMP.

       PROCEDURE DIVISION.
       MAIN-LOGIC.
           PERFORM GET-REQUEST
           PERFORM PROCESS-INQUIRY
           PERFORM PUT-RESPONSE
           EXEC CICS RETURN END-EXEC.

       GET-REQUEST.
      *    Retrieve the deserialized request from CICS container
           MOVE LENGTH OF DFHWS-BALANCE-REQUEST
               TO WS-DATA-LENGTH
           EXEC CICS GET CONTAINER(WS-INPUT-CONTAINER)
               CHANNEL(WS-CHANNEL-NAME)
               INTO(DFHWS-BALANCE-REQUEST)
               FLENGTH(WS-DATA-LENGTH)
               RESP(WS-RESP)
           END-EXEC

           IF WS-RESP NOT = DFHRESP(NORMAL)
               DISPLAY "Failed to get request container"
               MOVE "9999" TO DFHWS-STATUS-CODE
               MOVE "Internal processing error"
                   TO DFHWS-STATUS-MSG
               PERFORM PUT-RESPONSE
               EXEC CICS RETURN END-EXEC
           END-IF.

       PROCESS-INQUIRY.
      *    In production, query DB2 for account data
      *    Simulated for demonstration
           MOVE DFHWS-ACCOUNT-NUMBER
               TO DFHWS-ACCOUNT-NUMBER OF DFHWS-BALANCE-RESPONSE
           MOVE "MARIA GARCIA"
               TO DFHWS-HOLDER-NAME
           MOVE 15234.50
               TO DFHWS-BALANCE-AMT
           MOVE 15234.50
               TO DFHWS-AVAILABLE-AMT
           MOVE "USD"
               TO DFHWS-CURRENCY
           MOVE "0000"
               TO DFHWS-STATUS-CODE
           MOVE "SUCCESS"
               TO DFHWS-STATUS-MSG.

       PUT-RESPONSE.
      *    Place the response into the CICS container
      *    CICS will serialize it into the SOAP response envelope
           MOVE LENGTH OF DFHWS-BALANCE-RESPONSE
               TO WS-DATA-LENGTH
           EXEC CICS PUT CONTAINER(WS-OUTPUT-CONTAINER)
               CHANNEL(WS-CHANNEL-NAME)
               FROM(DFHWS-BALANCE-RESPONSE)
               FLENGTH(WS-DATA-LENGTH)
               RESP(WS-RESP)
           END-EXEC.

38.5.3 CICS as a SOAP Service Consumer

When COBOL needs to call an external SOAP service, CICS provides the INVOKE WEBSERVICE command:

      * Calling an external SOAP service from COBOL/CICS
           EXEC CICS INVOKE WEBSERVICE(WS-SERVICE-NAME)
               CHANNEL(WS-CHANNEL-NAME)
               URI(WS-SERVICE-URI)
               URILENGTH(WS-URI-LEN)
               RESP(WS-RESP)
               RESP2(WS-RESP2)
           END-EXEC

The request and response data are exchanged through CICS channels and containers, following the same pattern as the service provider example above.


38.6 MQ (Message Queuing) Integration

38.6.1 IBM MQ and COBOL

IBM MQ (formerly MQSeries and WebSphere MQ) provides reliable asynchronous messaging between applications. For COBOL programs on z/OS, MQ integration is one of the most battle-tested and widely deployed integration patterns. MQ decouples the COBOL program from its consumers, allowing the mainframe to process messages at its own pace while external systems submit requests and receive responses through queues.

IBM-Specific Feature: IBM MQ is a commercial product from IBM. The MQI (Message Queue Interface) for COBOL is available on z/OS, Linux, and other platforms. GnuCOBOL can interface with MQ through C bindings if the MQ client libraries are installed.

38.6.2 COBOL MQ Producer

      ******************************************************************
      * COBOL MQ MESSAGE PRODUCER
      * Sends a transaction notification message to an MQ queue
      * for consumption by downstream systems.
      * Requires: IBM MQ for z/OS, Enterprise COBOL
      ******************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID. MQPROD1.

       DATA DIVISION.
       WORKING-STORAGE SECTION.

      * MQ API fields
       01  WS-HCONN                  PIC S9(9) BINARY.
       01  WS-HOBJ                   PIC S9(9) BINARY.
       01  WS-COMPCODE               PIC S9(9) BINARY.
       01  WS-REASON                 PIC S9(9) BINARY.
       01  WS-OPTIONS                PIC S9(9) BINARY.
       01  WS-BUFFER-LEN             PIC S9(9) BINARY.

      * MQ Object Descriptor
       01  WS-MQOD.
           COPY CMQODV.

      * MQ Message Descriptor
       01  WS-MQMD.
           COPY CMQMDV.

      * MQ Put Message Options
       01  WS-MQPMO.
           COPY CMQPMOV.

      * Queue Manager and Queue Names
       01  WS-QUEUE-MGR-NAME         PIC X(48)
                                     VALUE "BANKQM01".
       01  WS-QUEUE-NAME             PIC X(48)
                                     VALUE "BANK.TXN.NOTIFY.Q".

      * Message payload (JSON format)
       01  WS-MSG-BUFFER             PIC X(2000).
       01  WS-MSG-LENGTH             PIC S9(9) BINARY.

      * Transaction data for the message
       01  WS-TXN-NOTIFICATION.
           05  transactionId         PIC X(20)
                                     VALUE "TXN-2025-00001234".
           05  accountNumber         PIC 9(10)
                                     VALUE 1000045678.
           05  transactionType       PIC X(10)
                                     VALUE "WITHDRAWAL".
           05  amount                PIC S9(11)V99
                                     VALUE -500.00.
           05  balanceAfter          PIC S9(11)V99
                                     VALUE 14734.50.
           05  transactionDate       PIC X(19)
                                     VALUE "2025-01-20T14:30:00".
           05  channelCode           PIC X(6)
                                     VALUE "ATM".

       PROCEDURE DIVISION.
       MAIN-LOGIC.
           PERFORM CONNECT-TO-QMGR
           PERFORM OPEN-QUEUE
           PERFORM BUILD-AND-SEND-MESSAGE
           PERFORM CLOSE-QUEUE
           PERFORM DISCONNECT-FROM-QMGR
           STOP RUN.

       CONNECT-TO-QMGR.
           CALL "MQCONN"
               USING WS-QUEUE-MGR-NAME
                     WS-HCONN
                     WS-COMPCODE
                     WS-REASON
           END-CALL
           IF WS-COMPCODE NOT = 0
               DISPLAY "MQCONN failed: RC=" WS-REASON
               STOP RUN
           END-IF.

       OPEN-QUEUE.
           MOVE WS-QUEUE-NAME TO MQOD-OBJECTNAME OF WS-MQOD
           MOVE 2064 TO WS-OPTIONS
      *    MQOO-OUTPUT = 16, MQOO-FAIL-IF-QUIESCING = 2048
           CALL "MQOPEN"
               USING WS-HCONN
                     WS-MQOD
                     WS-OPTIONS
                     WS-HOBJ
                     WS-COMPCODE
                     WS-REASON
           END-CALL
           IF WS-COMPCODE NOT = 0
               DISPLAY "MQOPEN failed: RC=" WS-REASON
               PERFORM DISCONNECT-FROM-QMGR
               STOP RUN
           END-IF.

       BUILD-AND-SEND-MESSAGE.
      *    Generate JSON message payload
           JSON GENERATE WS-MSG-BUFFER
               FROM WS-TXN-NOTIFICATION
               COUNT IN WS-MSG-LENGTH
           END-JSON

      *    Set message descriptor fields
           MOVE "MQSTR   " TO MQMD-FORMAT OF WS-MQMD
           MOVE 8 TO MQMD-PERSISTENCE OF WS-MQMD
      *    MQPER-PERSISTENT = 1
           MOVE 1 TO MQMD-PERSISTENCE OF WS-MQMD

      *    Put the message
           CALL "MQPUT"
               USING WS-HCONN
                     WS-HOBJ
                     WS-MQMD
                     WS-MQPMO
                     WS-MSG-LENGTH
                     WS-MSG-BUFFER
                     WS-COMPCODE
                     WS-REASON
           END-CALL
           IF WS-COMPCODE = 0
               DISPLAY "Message sent successfully"
               DISPLAY "  MsgId: " MQMD-MSGID OF WS-MQMD
           ELSE
               DISPLAY "MQPUT failed: RC=" WS-REASON
           END-IF.

       CLOSE-QUEUE.
           MOVE 0 TO WS-OPTIONS
           CALL "MQCLOSE"
               USING WS-HCONN
                     WS-HOBJ
                     WS-OPTIONS
                     WS-COMPCODE
                     WS-REASON
           END-CALL.

       DISCONNECT-FROM-QMGR.
           CALL "MQDISC"
               USING WS-HCONN
                     WS-COMPCODE
                     WS-REASON
           END-CALL.

38.6.3 COBOL MQ Consumer

      ******************************************************************
      * COBOL MQ MESSAGE CONSUMER
      * Reads transaction request messages from an MQ queue,
      * processes them, and sends reply messages.
      * Requires: IBM MQ for z/OS, Enterprise COBOL
      ******************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID. MQCONS1.

       DATA DIVISION.
       WORKING-STORAGE SECTION.

      * MQ API fields
       01  WS-HCONN                  PIC S9(9) BINARY.
       01  WS-HOBJ-INPUT             PIC S9(9) BINARY.
       01  WS-HOBJ-REPLY             PIC S9(9) BINARY.
       01  WS-COMPCODE               PIC S9(9) BINARY.
       01  WS-REASON                 PIC S9(9) BINARY.
       01  WS-OPTIONS                PIC S9(9) BINARY.
       01  WS-BUFFER-LEN             PIC S9(9) BINARY.
       01  WS-DATA-LEN               PIC S9(9) BINARY.

       01  WS-MQOD.
           COPY CMQODV.
       01  WS-MQMD.
           COPY CMQMDV.
       01  WS-MQGMO.
           COPY CMQGMOV.
       01  WS-MQPMO.
           COPY CMQPMOV.

       01  WS-INPUT-QUEUE            PIC X(48)
                                     VALUE "BANK.TXN.REQUEST.Q".
       01  WS-QUEUE-MGR              PIC X(48)
                                     VALUE "BANKQM01".
       01  WS-MSG-BUFFER             PIC X(4000).
       01  WS-REPLY-BUFFER           PIC X(2000).
       01  WS-REPLY-LEN              PIC S9(9) BINARY.
       01  WS-CONTINUE-FLAG          PIC 9 VALUE 1.
           88 CONTINUE-PROCESSING    VALUE 1.
           88 STOP-PROCESSING        VALUE 0.

      * Parsed request
       01  WS-TXN-REQUEST.
           05  requestType           PIC X(10).
           05  accountNumber         PIC 9(10).
           05  amount                PIC S9(11)V99.

      * Response data
       01  WS-TXN-RESPONSE.
           05  responseCode          PIC X(4).
           05  responseMessage       PIC X(50).
           05  resultBalance         PIC S9(11)V99.

       PROCEDURE DIVISION.
       MAIN-LOGIC.
           PERFORM CONNECT-AND-OPEN
           PERFORM PROCESS-MESSAGES
               UNTIL NOT CONTINUE-PROCESSING
           PERFORM CLEANUP
           STOP RUN.

       CONNECT-AND-OPEN.
           CALL "MQCONN"
               USING WS-QUEUE-MGR
                     WS-HCONN
                     WS-COMPCODE
                     WS-REASON
           END-CALL
           IF WS-COMPCODE NOT = 0
               DISPLAY "MQCONN failed: " WS-REASON
               STOP RUN
           END-IF

           MOVE WS-INPUT-QUEUE TO MQOD-OBJECTNAME OF WS-MQOD
           MOVE 2049 TO WS-OPTIONS
      *    MQOO-INPUT-AS-Q-DEF(1) + MQOO-FAIL-IF-QUIESCING(2048)
           CALL "MQOPEN"
               USING WS-HCONN
                     WS-MQOD
                     WS-OPTIONS
                     WS-HOBJ-INPUT
                     WS-COMPCODE
                     WS-REASON
           END-CALL
           IF WS-COMPCODE NOT = 0
               DISPLAY "MQOPEN failed: " WS-REASON
               SET STOP-PROCESSING TO TRUE
           END-IF.

       PROCESS-MESSAGES.
      *    Wait up to 30 seconds for a message
           MOVE 30000 TO MQGMO-WAITINTERVAL OF WS-MQGMO
           MOVE 4000 TO WS-BUFFER-LEN

           CALL "MQGET"
               USING WS-HCONN
                     WS-HOBJ-INPUT
                     WS-MQMD
                     WS-MQGMO
                     WS-BUFFER-LEN
                     WS-MSG-BUFFER
                     WS-DATA-LEN
                     WS-COMPCODE
                     WS-REASON
           END-CALL

           EVALUATE WS-COMPCODE
               WHEN 0
                   PERFORM HANDLE-MESSAGE
               WHEN OTHER
                   IF WS-REASON = 2033
      *                MQRC-NO-MSG-AVAILABLE - timeout
                       DISPLAY "No messages - waiting..."
                   ELSE
                       DISPLAY "MQGET failed: " WS-REASON
                       SET STOP-PROCESSING TO TRUE
                   END-IF
           END-EVALUATE.

       HANDLE-MESSAGE.
           JSON PARSE WS-MSG-BUFFER(1:WS-DATA-LEN)
               INTO WS-TXN-REQUEST
               ON EXCEPTION
                   DISPLAY "Failed to parse request message"
                   MOVE "9001" TO responseCode
                   MOVE "Invalid message format"
                       TO responseMessage
               NOT ON EXCEPTION
                   PERFORM PROCESS-TRANSACTION
           END-JSON

      *    Send reply if a reply queue was specified
           IF MQMD-REPLYTOQ OF WS-MQMD NOT = SPACES
               PERFORM SEND-REPLY
           END-IF.

       PROCESS-TRANSACTION.
           EVALUATE requestType
               WHEN "BALANCE"
      *            Simulated balance inquiry
                   MOVE "0000" TO responseCode
                   MOVE "Balance retrieved successfully"
                       TO responseMessage
                   MOVE 15234.50 TO resultBalance
               WHEN "DEPOSIT"
                   MOVE "0000" TO responseCode
                   MOVE "Deposit processed"
                       TO responseMessage
                   COMPUTE resultBalance =
                       15234.50 + amount
               WHEN "WITHDRAW"
                   IF amount > 15234.50
                       MOVE "1001" TO responseCode
                       MOVE "Insufficient funds"
                           TO responseMessage
                       MOVE 15234.50 TO resultBalance
                   ELSE
                       MOVE "0000" TO responseCode
                       MOVE "Withdrawal processed"
                           TO responseMessage
                       COMPUTE resultBalance =
                           15234.50 - amount
                   END-IF
               WHEN OTHER
                   MOVE "9002" TO responseCode
                   MOVE "Unknown request type"
                       TO responseMessage
           END-EVALUATE.

       SEND-REPLY.
           JSON GENERATE WS-REPLY-BUFFER
               FROM WS-TXN-RESPONSE
               COUNT IN WS-REPLY-LEN
           END-JSON

      *    Set up reply queue and send
           MOVE MQMD-REPLYTOQ OF WS-MQMD
               TO MQOD-OBJECTNAME OF WS-MQOD
           MOVE 2064 TO WS-OPTIONS
           CALL "MQOPEN"
               USING WS-HCONN
                     WS-MQOD
                     WS-OPTIONS
                     WS-HOBJ-REPLY
                     WS-COMPCODE
                     WS-REASON
           END-CALL
           IF WS-COMPCODE = 0
               CALL "MQPUT"
                   USING WS-HCONN
                         WS-HOBJ-REPLY
                         WS-MQMD
                         WS-MQPMO
                         WS-REPLY-LEN
                         WS-REPLY-BUFFER
                         WS-COMPCODE
                         WS-REASON
               END-CALL
               MOVE 0 TO WS-OPTIONS
               CALL "MQCLOSE"
                   USING WS-HCONN
                         WS-HOBJ-REPLY
                         WS-OPTIONS
                         WS-COMPCODE
                         WS-REASON
               END-CALL
           END-IF.

       CLEANUP.
           MOVE 0 TO WS-OPTIONS
           CALL "MQCLOSE"
               USING WS-HCONN
                     WS-HOBJ-INPUT
                     WS-OPTIONS
                     WS-COMPCODE
                     WS-REASON
           END-CALL
           CALL "MQDISC"
               USING WS-HCONN
                     WS-COMPCODE
                     WS-REASON
           END-CALL.

38.7 COBOL as a Microservice

38.7.1 Wrapping COBOL Behind API Facades

The microservice architecture structures an application as a collection of loosely coupled services, each owning its own data and communicating through well-defined APIs. COBOL programs can participate in this architecture by being wrapped behind lightweight API facades that translate between the microservice communication protocols (HTTP/JSON) and the COBOL program's native interface (COMMAREA, channels, or file-based).

The wrapping approach preserves the COBOL program's logic unchanged while making it accessible to the rest of the microservice ecosystem.

38.7.2 Architecture Patterns

Pattern 1: API Gateway + CICS

Mobile App --> API Gateway --> CICS Web Services --> COBOL Program
                                                          |
                                                    DB2 / VSAM

The API gateway (such as IBM DataPower, Kong, or AWS API Gateway) handles authentication, rate limiting, and protocol translation. CICS exposes the COBOL program as a web service. This is the most common pattern for z/OS-based COBOL.

Pattern 2: Sidecar Adapter

Microservice Mesh
    |
Sidecar (Node.js/Python) --> COBOL Program (batch or daemon)
    |                              |
Service Registry              File / DB

A lightweight sidecar application written in Node.js, Python, or Go handles HTTP/JSON communication and translates it into COBOL-compatible calls (file drops, MQ messages, or direct CALL).

Pattern 3: Event-Driven

Event Bus (Kafka/MQ)
    |
COBOL Consumer --> Process --> COBOL Producer
    |                               |
    DB2                        Event Bus

The COBOL program consumes events from a message broker, processes them, and publishes result events back to the broker. This fully decouples the COBOL program from its callers.

38.7.3 Sidecar Implementation Example

The following shows a Node.js sidecar that wraps a COBOL program as a REST microservice. The COBOL program is compiled to a shared library using GnuCOBOL and called via Node.js's foreign function interface.

The COBOL program (compiled as a shared library with GnuCOBOL):

      ******************************************************************
      * BALANCE INQUIRY SERVICE - GnuCOBOL Shared Library
      * Called by the Node.js sidecar via FFI.
      * Compile: cobc -m -o balancesvc.so balancesvc.cob
      * Compatible with: GnuCOBOL 2.x+
      ******************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID. balancesvc.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-ACCOUNTS.
           05  WS-ACCT-ENTRY OCCURS 5 TIMES.
               10  WS-A-NUM          PIC 9(10).
               10  WS-A-NAME         PIC X(30).
               10  WS-A-BAL          PIC S9(11)V99.
               10  WS-A-TYPE         PIC X(10).
       01  WS-INITIALIZED            PIC 9 VALUE 0.
       01  WS-IDX                    PIC 9(2).

       LINKAGE SECTION.
       01  LS-REQUEST.
           05  LS-REQ-ACCT-NUM      PIC 9(10).
           05  LS-REQ-OPERATION     PIC X(10).
           05  LS-REQ-AMOUNT        PIC S9(11)V99.
       01  LS-RESPONSE.
           05  LS-RSP-CODE          PIC X(4).
           05  LS-RSP-ACCT-NUM     PIC 9(10).
           05  LS-RSP-NAME          PIC X(30).
           05  LS-RSP-BALANCE       PIC S9(11)V99.
           05  LS-RSP-TYPE          PIC X(10).
           05  LS-RSP-MESSAGE       PIC X(50).

       PROCEDURE DIVISION USING LS-REQUEST LS-RESPONSE.
       MAIN-LOGIC.
           IF WS-INITIALIZED = 0
               PERFORM INITIALIZE-DATA
               MOVE 1 TO WS-INITIALIZED
           END-IF

           INITIALIZE LS-RESPONSE
           PERFORM FIND-ACCOUNT
           GOBACK.

       INITIALIZE-DATA.
           MOVE 1000045678 TO WS-A-NUM(1)
           MOVE "MARIA GARCIA" TO WS-A-NAME(1)
           MOVE 15234.50 TO WS-A-BAL(1)
           MOVE "SAVINGS" TO WS-A-TYPE(1)

           MOVE 1000045679 TO WS-A-NUM(2)
           MOVE "JOHN SMITH" TO WS-A-NAME(2)
           MOVE 3450.75 TO WS-A-BAL(2)
           MOVE "CHECKING" TO WS-A-TYPE(2)

           MOVE 1000045680 TO WS-A-NUM(3)
           MOVE "JANE DOE" TO WS-A-NAME(3)
           MOVE 87500.00 TO WS-A-BAL(3)
           MOVE "SAVINGS" TO WS-A-TYPE(3)

           MOVE 0 TO WS-A-NUM(4)
           MOVE 0 TO WS-A-NUM(5).

       FIND-ACCOUNT.
           PERFORM VARYING WS-IDX FROM 1 BY 1
               UNTIL WS-IDX > 5
               OR WS-A-NUM(WS-IDX) = 0
               IF WS-A-NUM(WS-IDX) = LS-REQ-ACCT-NUM
                   MOVE "0000" TO LS-RSP-CODE
                   MOVE WS-A-NUM(WS-IDX) TO LS-RSP-ACCT-NUM
                   MOVE WS-A-NAME(WS-IDX) TO LS-RSP-NAME
                   MOVE WS-A-BAL(WS-IDX) TO LS-RSP-BALANCE
                   MOVE WS-A-TYPE(WS-IDX) TO LS-RSP-TYPE
                   MOVE "Account found" TO LS-RSP-MESSAGE
                   EXIT PERFORM
               END-IF
           END-PERFORM

           IF LS-RSP-CODE = SPACES
               MOVE "1004" TO LS-RSP-CODE
               MOVE "Account not found" TO LS-RSP-MESSAGE
           END-IF.

38.8 z/OS Connect EE

38.8.1 Exposing COBOL Programs as REST APIs

IBM z/OS Connect Enterprise Edition (z/OS Connect EE) is IBM's strategic product for exposing z/OS-based assets, including COBOL programs running under CICS, IMS, or batch, as RESTful APIs. It provides a higher-level abstraction than coding CICS Web Services directly, with tooling for API mapping, request/response transformation, and Swagger/OpenAPI specification generation.

IBM-Specific Feature: z/OS Connect EE is an IBM licensed product that runs on z/OS. It is not available for other platforms or open-source environments.

38.8.2 How z/OS Connect EE Works

The architecture of z/OS Connect EE consists of three layers:

  1. API Layer: Defines the RESTful API (URL paths, HTTP methods, request/response schemas) using OpenAPI specifications.

  2. Service Layer: Maps between the API's JSON schema and the COBOL program's data structures (COMMAREA or channels).

  3. Service Provider: Connects to the z/OS subsystem (CICS, IMS, or batch) that hosts the COBOL program.

External Client
    |
    | HTTPS + JSON
    v
z/OS Connect EE Server
    |
    | API Mapping (JSON <--> COBOL copybook)
    v
Service Archive (.sar)
    |
    | CICS/IMS/Batch connector
    v
COBOL Program
    |
    v
DB2 / VSAM / IMS DB

38.8.3 Creating an API with z/OS Connect EE

The process involves several steps performed through the z/OS Connect EE API Editor (a graphical tool) or through command-line tooling:

Step 1: Create a Service Archive (.sar)

The service archive maps COBOL copybook structures to JSON schemas. You provide the COBOL copybook (the same one the program uses for its COMMAREA) and the tool generates the JSON mapping.

Given this COBOL copybook for a transfer service:

      ******************************************************************
      * COPYBOOK: XFERSVC - Transfer Service COMMAREA
      * Used by z/OS Connect EE for API mapping
      ******************************************************************
       01  XFER-COMMAREA.
           05  XFER-REQUEST.
               10  XFER-SOURCE-ACCT     PIC 9(10).
               10  XFER-TARGET-ACCT     PIC 9(10).
               10  XFER-AMOUNT          PIC S9(11)V99 COMP-3.
               10  XFER-CURRENCY        PIC X(3).
               10  XFER-MEMO            PIC X(50).
           05  XFER-RESPONSE.
               10  XFER-RESULT-CODE     PIC X(4).
               10  XFER-RESULT-MSG      PIC X(50).
               10  XFER-CONFIRM-NUM     PIC X(16).
               10  XFER-SOURCE-BAL      PIC S9(11)V99 COMP-3.
               10  XFER-TARGET-BAL      PIC S9(11)V99 COMP-3.
               10  XFER-TIMESTAMP       PIC X(26).

z/OS Connect EE generates this OpenAPI specification:

{
  "openapi": "3.0.0",
  "info": {
    "title": "Account Transfer API",
    "version": "1.0.0",
    "description": "Transfer funds between bank accounts"
  },
  "paths": {
    "/api/v1/transfers": {
      "post": {
        "summary": "Initiate a fund transfer",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TransferRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transfer processed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransferResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "TransferRequest": {
        "type": "object",
        "properties": {
          "sourceAccount": { "type": "string", "maxLength": 10 },
          "targetAccount": { "type": "string", "maxLength": 10 },
          "amount": { "type": "number" },
          "currency": { "type": "string", "maxLength": 3 },
          "memo": { "type": "string", "maxLength": 50 }
        }
      },
      "TransferResponse": {
        "type": "object",
        "properties": {
          "resultCode": { "type": "string" },
          "resultMessage": { "type": "string" },
          "confirmationNumber": { "type": "string" },
          "sourceBalance": { "type": "number" },
          "targetBalance": { "type": "number" },
          "timestamp": { "type": "string" }
        }
      }
    }
  }
}

Step 2: The COBOL Program (Unchanged)

The COBOL program that processes the transfer does not need to know anything about REST, JSON, or HTTP. It receives the COMMAREA from CICS as it always has:

      ******************************************************************
      * TRANSFER SERVICE PROGRAM
      * Processes fund transfers between accounts.
      * This program is unaware it is being called via REST API.
      * Requires: IBM CICS TS, Enterprise COBOL
      ******************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID. XFERPGM.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-SOURCE-BALANCE         PIC S9(11)V99.
       01  WS-TARGET-BALANCE         PIC S9(11)V99.
       01  WS-CONFIRM-CTR            PIC 9(8) VALUE 0.
       01  WS-CURRENT-TIMESTAMP      PIC X(26).
       01  WS-ABSTIME                PIC S9(15) COMP-3.
       01  WS-RESP                   PIC S9(8) COMP.

       COPY XFERSVC.

       PROCEDURE DIVISION.
       MAIN-LOGIC.
      *    Receive the COMMAREA from CICS
           EXEC CICS ADDRESS
               COMMAREA(ADDRESS OF XFER-COMMAREA)
               RESP(WS-RESP)
           END-EXEC

      *    Validate the request
           IF XFER-AMOUNT <= 0
               MOVE "1001" TO XFER-RESULT-CODE
               MOVE "Invalid transfer amount"
                   TO XFER-RESULT-MSG
               EXEC CICS RETURN END-EXEC
           END-IF

           IF XFER-SOURCE-ACCT = XFER-TARGET-ACCT
               MOVE "1002" TO XFER-RESULT-CODE
               MOVE "Source and target cannot be the same"
                   TO XFER-RESULT-MSG
               EXEC CICS RETURN END-EXEC
           END-IF

      *    In production: Read source account from DB2, verify
      *    funds, debit source, credit target, write audit log.
      *    Simulated here for brevity:

           MOVE 15234.50 TO WS-SOURCE-BALANCE
           IF XFER-AMOUNT > WS-SOURCE-BALANCE
               MOVE "1003" TO XFER-RESULT-CODE
               MOVE "Insufficient funds"
                   TO XFER-RESULT-MSG
               EXEC CICS RETURN END-EXEC
           END-IF

           SUBTRACT XFER-AMOUNT FROM WS-SOURCE-BALANCE
           MOVE 8750.00 TO WS-TARGET-BALANCE
           ADD XFER-AMOUNT TO WS-TARGET-BALANCE

      *    Generate confirmation number
           ADD 1 TO WS-CONFIRM-CTR
           STRING "XFER" WS-CONFIRM-CTR
               DELIMITED BY SIZE
               INTO XFER-CONFIRM-NUM
           END-STRING

      *    Get current timestamp
           EXEC CICS ASKTIME
               ABSTIME(WS-ABSTIME)
               RESP(WS-RESP)
           END-EXEC
           EXEC CICS FORMATTIME
               ABSTIME(WS-ABSTIME)
               YYYY(WS-CURRENT-TIMESTAMP(1:4))
               MM(WS-CURRENT-TIMESTAMP(6:2))
               DD(WS-CURRENT-TIMESTAMP(9:2))
               TIME(WS-CURRENT-TIMESTAMP(12:8))
               RESP(WS-RESP)
           END-EXEC

           MOVE "0000" TO XFER-RESULT-CODE
           MOVE "Transfer successful" TO XFER-RESULT-MSG
           MOVE WS-SOURCE-BALANCE TO XFER-SOURCE-BAL
           MOVE WS-TARGET-BALANCE TO XFER-TARGET-BAL
           MOVE WS-CURRENT-TIMESTAMP TO XFER-TIMESTAMP

           EXEC CICS RETURN END-EXEC.

The power of z/OS Connect EE is that this program requires zero changes. The API layer handles all JSON parsing, HTTP protocol management, and data transformation.


38.9 gRPC and COBOL

38.9.1 Emerging Integration Patterns

gRPC is a high-performance RPC framework that uses Protocol Buffers for serialization and HTTP/2 for transport. While there is no native COBOL gRPC library, several integration approaches are emerging:

Pattern 1: gRPC Gateway with COBOL Backend

A gRPC gateway (written in Go, Java, or C++) receives gRPC calls, translates them into COBOL-compatible formats (COMMAREA, MQ messages, or file records), invokes the COBOL program, and returns the response through the gRPC channel. This is architecturally similar to the sidecar pattern described in Section 38.7.

Pattern 2: gRPC-to-REST Transcoding

Many gRPC frameworks support automatic transcoding between gRPC and REST. By first exposing COBOL programs as REST APIs (via z/OS Connect EE or CICS Web Services), a gRPC transcoding proxy can make them accessible through gRPC as well.

Pattern 3: Protocol Buffers via C Interface

GnuCOBOL's C interoperability allows COBOL programs to call C libraries that implement Protocol Buffer serialization. The COBOL program populates a C structure, the C library serializes it to Protocol Buffer format, and a separate gRPC client sends it over the network.

      ******************************************************************
      * GRPC INTEGRATION CONCEPT - Using C bridge
      * Demonstrates how COBOL can prepare data for gRPC
      * transmission through a C intermediary.
      * Compatible with: GnuCOBOL 2.x+ with custom C bridge
      ******************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID. GRPCBRIDGE.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-ACCT-NUM              PIC 9(10) VALUE 1000045678.
       01  WS-BALANCE               PIC S9(11)V99 VALUE 15234.50.
       01  WS-HOLDER-NAME           PIC X(30)
                                    VALUE "MARIA GARCIA".
       01  WS-RESULT                PIC S9(4) COMP-5.
       01  WS-BALANCE-INT           PIC S9(9) COMP-5.
       01  WS-BALANCE-FRAC          PIC S9(4) COMP-5.
       01  WS-NAME-WITH-NULL        PIC X(31).

       PROCEDURE DIVISION.
       MAIN-LOGIC.
      *    Prepare data for the C gRPC bridge
           COMPUTE WS-BALANCE-INT =
               FUNCTION INTEGER-PART(WS-BALANCE)
           COMPUTE WS-BALANCE-FRAC =
               (WS-BALANCE - WS-BALANCE-INT) * 100

           STRING WS-HOLDER-NAME DELIMITED BY "  "
                  X"00" DELIMITED BY SIZE
                  INTO WS-NAME-WITH-NULL
           END-STRING

      *    Call C function that sends gRPC message
           CALL "grpc_send_balance"
               USING BY REFERENCE WS-ACCT-NUM
                     BY REFERENCE WS-NAME-WITH-NULL
                     BY VALUE WS-BALANCE-INT
                     BY VALUE WS-BALANCE-FRAC
               RETURNING WS-RESULT
           END-CALL

           IF WS-RESULT = 0
               DISPLAY "gRPC message sent successfully"
           ELSE
               DISPLAY "gRPC send failed: " WS-RESULT
           END-IF
           STOP RUN.

38.10 Container-Based COBOL

38.10.1 COBOL in Docker

GnuCOBOL compiles COBOL source code into native executables that can run on Linux. This makes it straightforward to package COBOL programs in Docker containers, enabling them to participate in containerized deployments alongside other microservices.

GnuCOBOL Feature: Container-based deployment is primarily relevant to GnuCOBOL users. IBM mainframe COBOL runs on z/OS, which has its own container strategy through IBM Cloud Pak for z/OS and z/OS Container Extensions (zCX).

Here is a Dockerfile that builds and runs a COBOL banking service:

# Dockerfile for COBOL microservice
# Uses GnuCOBOL compiler on Alpine Linux

FROM alpine:3.18 AS builder

# Install GnuCOBOL compiler and dependencies
RUN apk add --no-cache \
    gnucobol \
    gcc \
    musl-dev \
    gmp-dev \
    db-dev \
    ncurses-dev

# Copy COBOL source
WORKDIR /app
COPY src/*.cob /app/src/
COPY copybooks/*.cpy /app/copybooks/

# Compile the COBOL programs
RUN cobc -x -o /app/bin/balancesvc \
    -I /app/copybooks \
    /app/src/balancesvc.cob

RUN cobc -x -o /app/bin/txnprocessor \
    -I /app/copybooks \
    /app/src/txnprocessor.cob

# Runtime stage - smaller image
FROM alpine:3.18

RUN apk add --no-cache \
    libcob \
    gmp \
    db

WORKDIR /app
COPY --from=builder /app/bin/ /app/bin/

# Non-root user for security
RUN adduser -D -H coboluser
USER coboluser

EXPOSE 8080
CMD ["/app/bin/balancesvc"]

38.10.2 Kubernetes Deployment

Once the COBOL service is containerized, it can be deployed to Kubernetes like any other microservice:

# kubernetes/cobol-balance-service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cobol-balance-service
  labels:
    app: balance-service
    tier: backend
    language: cobol
spec:
  replicas: 3
  selector:
    matchLabels:
      app: balance-service
  template:
    metadata:
      labels:
        app: balance-service
    spec:
      containers:
      - name: cobol-balance
        image: bankregistry.example.com/cobol-balance:1.0.0
        ports:
        - containerPort: 8080
        resources:
          requests:
            memory: "64Mi"
            cpu: "100m"
          limits:
            memory: "128Mi"
            cpu: "250m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 3
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: cobol-balance-service
spec:
  selector:
    app: balance-service
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
  type: ClusterIP

Notable points about running COBOL in Kubernetes:

  • COBOL executables compiled by GnuCOBOL are native binaries, so the containers are small and start quickly compared to Java-based containers.
  • COBOL programs are not inherently stateless. If the program maintains state in WORKING-STORAGE across calls (as many batch programs do), you must design the container wrapper to handle state management, either externalizing state to a database or ensuring sticky sessions.
  • The liveness and readiness probes require a health-check endpoint, which the COBOL program itself does not natively provide. The sidecar or wrapper application should implement these endpoints.

38.11 Cloud Integration

38.11.1 COBOL and Cloud Platforms

Each major cloud provider offers pathways for integrating with or running COBOL workloads:

AWS (Amazon Web Services): - AWS Mainframe Modernization service provides tools for rehosting COBOL applications on AWS using Micro Focus or Blu Age. - AWS Lambda can invoke COBOL programs compiled as shared libraries through custom runtimes. - Amazon MQ provides managed message queuing compatible with IBM MQ protocols. - API Gateway can front-end COBOL services deployed on EC2 or ECS containers.

Microsoft Azure: - Azure supports Micro Focus Enterprise Server for running COBOL workloads in Azure VMs. - Azure API Management can expose COBOL services as managed APIs. - Azure Service Bus provides messaging integration compatible with enterprise patterns. - Azure DevOps can incorporate COBOL compile and test steps in CI/CD pipelines.

Google Cloud Platform (GCP): - GCP's Dual Run service allows running mainframe workloads in parallel on GCP for validation. - Cloud Run can host containerized GnuCOBOL applications. - Pub/Sub provides event-driven integration patterns for COBOL services.

38.11.2 Hybrid Cloud Architecture

Most organizations adopt a hybrid approach where mainframe COBOL continues to run on z/OS while cloud-native components interact with it through APIs and messaging:

Cloud Environment (AWS/Azure/GCP)
    |
    | HTTPS / MQ / Event Streaming
    |
API Gateway / Message Bridge
    |
    | Secure network link (Direct Connect / ExpressRoute)
    |
On-Premise z/OS
    |
z/OS Connect EE / CICS / IMS
    |
COBOL Programs + DB2

The critical considerations for this architecture are:

Latency: Network round-trips between cloud and mainframe add latency. Design for asynchronous communication (MQ, event streaming) where possible, and batch multiple operations in a single synchronous call when real-time response is needed.

Security: Data passing between cloud and mainframe must be encrypted in transit (TLS) and the connection must be authenticated. IBM z/OS supports AT-TLS for transparent TLS encryption, and z/OS Connect EE provides OAuth 2.0 and JWT token validation.

Data Consistency: When a transaction spans both cloud and mainframe components, ensure consistency through saga patterns or two-phase commit protocols. IBM MQ's transactional messaging provides exactly-once delivery guarantees that help maintain consistency.


38.12 Practical Example: Exposing a Legacy Banking Transaction as a REST API

38.12.1 Scenario Description

A bank has a COBOL program called ACCTXFER that has processed inter-account fund transfers for 25 years. It runs under CICS, receives a COMMAREA with transfer details, validates the accounts, checks balances, performs the debit and credit against DB2, writes an audit record, and returns a result. The mobile banking team needs to call this program from their REST-based mobile backend.

The goal is to expose ACCTXFER as a REST API without modifying the COBOL program.

38.12.2 The Existing COBOL Program

      ******************************************************************
      * ACCTXFER - Inter-Account Fund Transfer
      * This program has been in production for 25 years.
      * It must NOT be modified for the API integration.
      * Requires: IBM CICS TS, Enterprise COBOL, DB2
      ******************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID. ACCTXFER.

       DATA DIVISION.
       WORKING-STORAGE SECTION.

      * DB2 communication area
           EXEC SQL INCLUDE SQLCA END-EXEC.

       01  WS-SOURCE-RECORD.
           05  WS-SRC-ACCT-NUM      PIC 9(10).
           05  WS-SRC-HOLDER        PIC X(30).
           05  WS-SRC-BALANCE       PIC S9(11)V99 COMP-3.
           05  WS-SRC-STATUS        PIC X.

       01  WS-TARGET-RECORD.
           05  WS-TGT-ACCT-NUM      PIC 9(10).
           05  WS-TGT-HOLDER        PIC X(30).
           05  WS-TGT-BALANCE       PIC S9(11)V99 COMP-3.
           05  WS-TGT-STATUS        PIC X.

       01  WS-TIMESTAMP              PIC X(26).
       01  WS-ABSTIME                PIC S9(15) COMP-3.
       01  WS-CONFIRM-SEQ            PIC 9(8) VALUE 0.
       01  WS-RESP                   PIC S9(8) COMP.

       LINKAGE SECTION.
       01  DFHCOMMAREA.
           05  CA-REQUEST.
               10  CA-SOURCE-ACCT   PIC 9(10).
               10  CA-TARGET-ACCT   PIC 9(10).
               10  CA-AMOUNT        PIC S9(11)V99 COMP-3.
               10  CA-CURRENCY      PIC X(3).
               10  CA-MEMO          PIC X(50).
               10  CA-REQUESTOR-ID  PIC X(8).
           05  CA-RESPONSE.
               10  CA-RESULT-CODE   PIC X(4).
               10  CA-RESULT-MSG    PIC X(50).
               10  CA-CONFIRM-NUM   PIC X(16).
               10  CA-SRC-NEW-BAL   PIC S9(11)V99 COMP-3.
               10  CA-TGT-NEW-BAL   PIC S9(11)V99 COMP-3.
               10  CA-TXN-TIMESTAMP PIC X(26).

       PROCEDURE DIVISION.
       MAIN-LOGIC.
           PERFORM VALIDATE-INPUT
           PERFORM RETRIEVE-ACCOUNTS
           PERFORM CHECK-BALANCES
           PERFORM EXECUTE-TRANSFER
           PERFORM WRITE-AUDIT-RECORD
           PERFORM BUILD-RESPONSE
           EXEC CICS RETURN END-EXEC.

       VALIDATE-INPUT.
           IF CA-AMOUNT <= 0
               MOVE "V001" TO CA-RESULT-CODE
               MOVE "Transfer amount must be positive"
                   TO CA-RESULT-MSG
               EXEC CICS RETURN END-EXEC
           END-IF

           IF CA-SOURCE-ACCT = CA-TARGET-ACCT
               MOVE "V002" TO CA-RESULT-CODE
               MOVE "Source and target must be different"
                   TO CA-RESULT-MSG
               EXEC CICS RETURN END-EXEC
           END-IF

           IF CA-CURRENCY NOT = "USD"
               AND CA-CURRENCY NOT = "EUR"
               AND CA-CURRENCY NOT = "GBP"
               MOVE "V003" TO CA-RESULT-CODE
               MOVE "Unsupported currency"
                   TO CA-RESULT-MSG
               EXEC CICS RETURN END-EXEC
           END-IF.

       RETRIEVE-ACCOUNTS.
           EXEC SQL
               SELECT ACCT_NUM, HOLDER_NAME,
                      BALANCE, STATUS
               INTO :WS-SRC-ACCT-NUM,
                    :WS-SRC-HOLDER,
                    :WS-SRC-BALANCE,
                    :WS-SRC-STATUS
               FROM ACCOUNTS
               WHERE ACCT_NUM = :CA-SOURCE-ACCT
           END-EXEC

           IF SQLCODE NOT = 0
               MOVE "A001" TO CA-RESULT-CODE
               MOVE "Source account not found"
                   TO CA-RESULT-MSG
               EXEC CICS RETURN END-EXEC
           END-IF

           IF WS-SRC-STATUS NOT = "A"
               MOVE "A002" TO CA-RESULT-CODE
               MOVE "Source account is not active"
                   TO CA-RESULT-MSG
               EXEC CICS RETURN END-EXEC
           END-IF

           EXEC SQL
               SELECT ACCT_NUM, HOLDER_NAME,
                      BALANCE, STATUS
               INTO :WS-TGT-ACCT-NUM,
                    :WS-TGT-HOLDER,
                    :WS-TGT-BALANCE,
                    :WS-TGT-STATUS
               FROM ACCOUNTS
               WHERE ACCT_NUM = :CA-TARGET-ACCT
           END-EXEC

           IF SQLCODE NOT = 0
               MOVE "A003" TO CA-RESULT-CODE
               MOVE "Target account not found"
                   TO CA-RESULT-MSG
               EXEC CICS RETURN END-EXEC
           END-IF

           IF WS-TGT-STATUS NOT = "A"
               MOVE "A004" TO CA-RESULT-CODE
               MOVE "Target account is not active"
                   TO CA-RESULT-MSG
               EXEC CICS RETURN END-EXEC
           END-IF.

       CHECK-BALANCES.
           IF CA-AMOUNT > WS-SRC-BALANCE
               MOVE "B001" TO CA-RESULT-CODE
               MOVE "Insufficient funds in source account"
                   TO CA-RESULT-MSG
               EXEC CICS RETURN END-EXEC
           END-IF.

       EXECUTE-TRANSFER.
      *    Debit source account
           EXEC SQL
               UPDATE ACCOUNTS
               SET BALANCE = BALANCE - :CA-AMOUNT
               WHERE ACCT_NUM = :CA-SOURCE-ACCT
           END-EXEC

           IF SQLCODE NOT = 0
               MOVE "X001" TO CA-RESULT-CODE
               MOVE "Failed to debit source account"
                   TO CA-RESULT-MSG
               EXEC SQL ROLLBACK END-EXEC
               EXEC CICS RETURN END-EXEC
           END-IF

      *    Credit target account
           EXEC SQL
               UPDATE ACCOUNTS
               SET BALANCE = BALANCE + :CA-AMOUNT
               WHERE ACCT_NUM = :CA-TARGET-ACCT
           END-EXEC

           IF SQLCODE NOT = 0
               MOVE "X002" TO CA-RESULT-CODE
               MOVE "Failed to credit target account"
                   TO CA-RESULT-MSG
               EXEC SQL ROLLBACK END-EXEC
               EXEC CICS RETURN END-EXEC
           END-IF

      *    Read back new balances
           EXEC SQL
               SELECT BALANCE
               INTO :WS-SRC-BALANCE
               FROM ACCOUNTS
               WHERE ACCT_NUM = :CA-SOURCE-ACCT
           END-EXEC
           MOVE WS-SRC-BALANCE TO CA-SRC-NEW-BAL

           EXEC SQL
               SELECT BALANCE
               INTO :WS-TGT-BALANCE
               FROM ACCOUNTS
               WHERE ACCT_NUM = :CA-TARGET-ACCT
           END-EXEC
           MOVE WS-TGT-BALANCE TO CA-TGT-NEW-BAL.

       WRITE-AUDIT-RECORD.
           EXEC CICS ASKTIME
               ABSTIME(WS-ABSTIME)
           END-EXEC
           EXEC CICS FORMATTIME
               ABSTIME(WS-ABSTIME)
               YYYY(WS-TIMESTAMP(1:4))
               MM(WS-TIMESTAMP(6:2))
               DD(WS-TIMESTAMP(9:2))
               TIME(WS-TIMESTAMP(12:8))
           END-EXEC

           EXEC SQL
               INSERT INTO TRANSFER_AUDIT
               (SOURCE_ACCT, TARGET_ACCT, AMOUNT,
                CURRENCY, MEMO, REQUESTOR,
                RESULT_CODE, TXN_TIMESTAMP)
               VALUES
               (:CA-SOURCE-ACCT, :CA-TARGET-ACCT,
                :CA-AMOUNT, :CA-CURRENCY, :CA-MEMO,
                :CA-REQUESTOR-ID,
                '0000', :WS-TIMESTAMP)
           END-EXEC.

       BUILD-RESPONSE.
           MOVE "0000" TO CA-RESULT-CODE
           MOVE "Transfer completed successfully"
               TO CA-RESULT-MSG

           ADD 1 TO WS-CONFIRM-SEQ
           STRING "XFR"
                  FUNCTION CURRENT-DATE(1:8)
                  WS-CONFIRM-SEQ
               DELIMITED BY SIZE
               INTO CA-CONFIRM-NUM
           END-STRING

           MOVE WS-TIMESTAMP TO CA-TXN-TIMESTAMP.

38.12.3 Integration Layer Configuration

The JCL to define the CICS resources for the API integration:

//*----------------------------------------------------------*
//* CICS RESOURCE DEFINITIONS FOR ACCTXFER API
//* Defines the URIMAP, PIPELINE, and WEBSERVICE resources
//* that expose ACCTXFER as a REST endpoint.
//* IBM-Specific: CICS TS V5.2+
//*----------------------------------------------------------*
//CSDUP    EXEC PGM=DFHCSDUP
//STEPLIB  DD DSN=CICSTS56.CICS.SDFHLOAD,DISP=SHR
//DFHCSD   DD DSN=CICSTS56.CICS.DFHCSD,DISP=SHR
//SYSPRINT DD SYSOUT=*
//SYSIN    DD *
  DEFINE URIMAP(XFERAPI)
         GROUP(BANKAPIS)
         USAGE(SERVER)
         SCHEME(HTTPS)
         HOST(*)
         PATH(/api/v1/transfers)
         PIPELINE(RESTPIPE)
         PROGRAM(ACCTXFER)
         TRANSACTION(XFER)
         DESCRIPTION(Account Transfer REST API)

  DEFINE PIPELINE(RESTPIPE)
         GROUP(BANKAPIS)
         CONFIGFILE(/var/cicsts/config/restpipe.xml)
         SHELF(/var/cicsts/shelf)
         DESCRIPTION(REST API Pipeline)

  DEFINE WEBSERVICE(XFERSVC)
         GROUP(BANKAPIS)
         PIPELINE(RESTPIPE)
         WSBIND(/var/cicsts/wsbind/xfersvc.wsbind)
         DESCRIPTION(Transfer Web Service Binding)
/*

38.12.4 Testing the Integrated API

Once deployed, the mobile backend team can call the COBOL transfer program as a standard REST API:

Request:

POST /api/v1/transfers HTTP/1.1
Host: mainframe-api.bank.internal
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

{
  "sourceAccount": "1000045678",
  "targetAccount": "2000098765",
  "amount": 250.00,
  "currency": "USD",
  "memo": "Monthly rent payment",
  "requestorId": "MOBILEAP"
}

Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "resultCode": "0000",
  "resultMessage": "Transfer completed successfully",
  "confirmationNumber": "XFR2025012000000001",
  "sourceNewBalance": 14984.50,
  "targetNewBalance": 9250.00,
  "transactionTimestamp": "2025-01-20T14:35:22.000000"
}

The 25-year-old COBOL program processes this request without any awareness that it was invoked through a REST API from a mobile phone. From the COBOL program's perspective, it received a COMMAREA through CICS exactly as it always has. The entire HTTP/JSON/REST layer is handled by CICS and z/OS Connect EE.

38.12.5 End-to-End Architecture

The complete architecture for this integration is:

Customer's Mobile Phone
    |
    | HTTPS + JSON
    v
Mobile Backend (Cloud)
    |
    | HTTPS + JSON (via API Gateway)
    v
IBM z/OS Connect EE
    |
    | JSON-to-COMMAREA mapping
    v
CICS TS (Transaction Server)
    |
    | COMMAREA
    v
ACCTXFER (COBOL Program)
    |
    | SQL
    v
DB2 (ACCOUNTS table, TRANSFER_AUDIT table)

Each layer in this architecture adds value:

  • Mobile Backend: Handles user authentication, session management, and mobile-specific logic.
  • API Gateway: Provides rate limiting, OAuth token validation, and API versioning.
  • z/OS Connect EE: Transforms JSON to/from COBOL data structures, generates OpenAPI documentation.
  • CICS: Provides transaction management, security, and workload balancing.
  • ACCTXFER: Encodes 25 years of tested business logic for fund transfers.
  • DB2: Provides ACID-compliant persistence for account data.

Summary

Integrating COBOL with modern systems is not about replacing COBOL but about extending its reach. The techniques covered in this chapter enable COBOL programs to participate fully in the API economy while preserving the reliability and business logic that decades of production use have validated. Key integration capabilities include:

  • JSON Processing: IBM Enterprise COBOL V6.1+ provides native JSON GENERATE and JSON PARSE statements. For GnuCOBOL and older compilers, manual string construction or C library bridges provide alternatives.

  • XML Processing: IBM Enterprise COBOL V3.4+ provides XML GENERATE and XML PARSE. XML remains important for SOAP services, financial messaging standards, and regulatory reporting.

  • REST API Integration: CICS Web Services commands allow COBOL programs to both consume and provide RESTful APIs. COBOL programs can call external REST services and be exposed as REST endpoints.

  • SOAP Web Services: CICS provides SOAP service provider and consumer capabilities through the Web Services Assistant tooling, which generates COBOL copybooks from WSDL definitions.

  • Message Queuing: IBM MQ provides reliable asynchronous communication between COBOL programs and external systems, decoupling producers and consumers for resilient integration.

  • Microservice Wrapping: COBOL programs can be wrapped behind API facades using CICS, z/OS Connect EE, or lightweight sidecars. The COBOL program itself does not need to change.

  • z/OS Connect EE: IBM's strategic product for exposing z/OS assets as REST APIs, providing JSON-to-COBOL transformation, OpenAPI specification generation, and API management.

  • Container Deployment: GnuCOBOL programs can be packaged in Docker containers and deployed to Kubernetes, enabling COBOL to participate in cloud-native architectures.

  • Cloud Integration: AWS, Azure, and GCP all provide pathways for COBOL integration, from rehosting to hybrid cloud architectures with secure network links.

The practical example of exposing the ACCTXFER program as a REST API demonstrates the core principle: the COBOL program does not change. Integration layers handle protocol translation, data format conversion, and API management, allowing the proven COBOL business logic to serve new channels and consumers without risk.


Review Questions

  1. Explain the difference between JSON GENERATE and JSON PARSE. What COBOL compiler version is required for each?

  2. Why is the XML PARSE statement event-driven (SAX-style) rather than tree-based (DOM-style)? What implications does this have for the processing procedure?

  3. Describe the three fundamental patterns for COBOL integration: service provider, service consumer, and event participant. When would you choose each?

  4. What role does the CICS COMMAREA play in the z/OS Connect EE architecture? Why does the COBOL program not need to know about JSON or HTTP?

  5. Compare the MQ-based integration pattern with the REST-based pattern. What are the advantages of asynchronous messaging for high-volume transaction processing?

  6. What are the limitations of running COBOL in Docker containers? How do stateful COBOL programs need to be adapted for containerized deployment?

  7. Why is hybrid cloud (mainframe plus public cloud) more common than full cloud migration for COBOL workloads? What technical and business factors drive this choice?

  8. In the ACCTXFER practical example, identify all the layers between the mobile phone and the DB2 database. Explain the responsibility of each layer.


Exercises

Exercise 38.1: Write a COBOL program (IBM Enterprise COBOL V6.1+) that uses JSON GENERATE to produce a JSON response containing an array of the last five transactions for an account. Define a COBOL data structure with an OCCURS clause to represent the transaction list and generate the corresponding JSON.

Exercise 38.2: Write an XML PARSE processing procedure that handles a payment initiation message in ISO 20022 pain.001 format. The procedure should extract the debtor account, creditor account, amount, and currency from the XML and populate a COBOL COMMAREA structure.

Exercise 38.3: Design a COBOL MQ consumer that reads account update messages from a queue, validates the update request, applies it to a VSAM file, and sends a confirmation message to a reply queue. Include error handling for invalid messages and queue access failures.

Exercise 38.4: Create a Dockerfile that compiles a GnuCOBOL banking service program and packages it with a Python Flask sidecar that exposes the COBOL logic as a REST API. The Flask application should translate JSON requests into COBOL-compatible calls using subprocess or ctypes.

Exercise 38.5: Write the COBOL copybook and z/OS Connect EE API specification (OpenAPI 3.0 JSON) for a loan payment service. The API should accept a loan account number, payment amount, and payment date, and return the updated loan balance, next payment due date, and remaining term in months. Map each COBOL copybook field to the corresponding JSON property.