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...
In This Chapter
- Part VIII - Modern COBOL and System Evolution
- 38.1 Why Integrate COBOL with Modern Systems
- 38.2 COBOL and JSON
- 38.3 COBOL and XML
- 38.4 RESTful API Integration via CICS Web Services
- 38.5 SOAP Web Services
- 38.6 MQ (Message Queuing) Integration
- 38.7 COBOL as a Microservice
- 38.8 z/OS Connect EE
- 38.9 gRPC and COBOL
- 38.10 Container-Based COBOL
- 38.11 Cloud Integration
- 38.12 Practical Example: Exposing a Legacy Banking Transaction as a REST API
- Summary
- Review Questions
- Exercises
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:
-
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.
-
COBOL as Service Consumer: The COBOL program calls external APIs or services to obtain data or trigger actions in other systems.
-
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:
- Define the service interface using WSDL (Web Services Description Language).
- Use the CICS Web Services Assistant tool (DFHLS2WS) to generate a COBOL copybook from the WSDL.
- Write a COBOL program that uses the generated copybook to receive the input message and produce the output message.
- 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:
-
API Layer: Defines the RESTful API (URL paths, HTTP methods, request/response schemas) using OpenAPI specifications.
-
Service Layer: Maps between the API's JSON schema and the COBOL program's data structures (COMMAREA or channels).
-
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
-
Explain the difference between JSON GENERATE and JSON PARSE. What COBOL compiler version is required for each?
-
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?
-
Describe the three fundamental patterns for COBOL integration: service provider, service consumer, and event participant. When would you choose each?
-
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?
-
Compare the MQ-based integration pattern with the REST-based pattern. What are the advantages of asynchronous messaging for high-volume transaction processing?
-
What are the limitations of running COBOL in Docker containers? How do stateful COBOL programs need to be adapted for containerized deployment?
-
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?
-
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.