> "A single-screen inquiry is where you learn CICS. A multi-screen maintenance transaction with scrolling, validation, and error recovery is where you become a CICS programmer."
In This Chapter
- 30.1 Temporary Storage Queues (TSQ): Your Scratch Pad
- 30.2 Transient Data Queues (TDQ): Event-Driven Processing
- 30.3 The START Command: Asynchronous Processing
- 30.4 LINK and XCTL: Program-to-Program Communication
- 30.5 Channel/Container Programming: The Modern Approach
- 30.6 Multi-Screen Transactions: Browse/Scroll Pattern
- 30.7 HANDLE ABEND: Error Recovery in CICS
- 30.8 CICS Web Services: Bridging Old and New
- 30.9 CICS Security: RACF Integration
- 30.10 GlobalBank Scenario: Multi-Screen Account Maintenance
- 30.11 MedClaim Scenario: Claim History Browse with Scrolling
- 30.12 Defensive Programming in CICS
- 30.13 Putting It All Together: Transaction Design Patterns
- Summary
Chapter 30: Advanced CICS Programming
"A single-screen inquiry is where you learn CICS. A multi-screen maintenance transaction with scrolling, validation, and error recovery is where you become a CICS programmer." — James Okafor, team lead, MedClaim
In Chapter 29, you learned the fundamentals: BMS maps, SEND MAP, RECEIVE MAP, pseudo-conversational design with COMMAREA, and basic error handling with RESP/RESP2. Those foundations are enough to build simple inquiry transactions. But production CICS applications demand far more — multi-screen workflows, scrollable browse lists, asynchronous processing, and robust error recovery.
This chapter advances your CICS skills to production level. You will learn to use Temporary Storage Queues (TSQs) and Transient Data Queues (TDQs) for data management, build multi-screen transactions with browse/scroll patterns, trigger asynchronous work with the START command, use modern channel/container programming, handle abends gracefully, and integrate CICS with web services.
30.1 Temporary Storage Queues (TSQ): Your Scratch Pad
A Temporary Storage Queue is a named, CICS-managed data store that persists across task boundaries. Unlike the COMMAREA (which is limited in size and passed between consecutive tasks), a TSQ can hold large amounts of data and be accessed by any task that knows its name.
Why TSQs Matter
The COMMAREA has a practical limit of about 32 KB. For a multi-screen transaction that needs to store a browse list of 500 account records, the COMMAREA is far too small. TSQs solve this by providing a flexible, indexed storage mechanism:
- Each item in the queue is numbered (item 1, item 2, item 3...)
- Any item can be read directly by its number
- Items can be added, read, updated, or the entire queue deleted
- TSQs survive across pseudo-conversational task boundaries
- TSQs can be in main storage (fast, volatile) or auxiliary storage (slower, recoverable)
Writing to a TSQ
WORKING-STORAGE SECTION.
01 WS-TSQ-NAME PIC X(8).
01 WS-TSQ-ITEM PIC S9(4) COMP.
01 WS-TSQ-DATA.
05 WS-TSQ-ACCT-NUM PIC X(10).
05 WS-TSQ-ACCT-NAME PIC X(50).
05 WS-TSQ-BALANCE PIC S9(11)V9(2) COMP-3.
01 WS-TSQ-LEN PIC S9(4) COMP VALUE 67.
01 WS-RESP PIC S9(8) COMP.
PROCEDURE DIVISION.
*--------------------------------------------------------------
* Build a unique queue name using terminal ID
*--------------------------------------------------------------
STRING 'BRW' EIBTRMID
DELIMITED BY SIZE
INTO WS-TSQ-NAME
END-STRING
*--------------------------------------------------------------
* Write first item (WRITEQ TS creates the queue)
*--------------------------------------------------------------
EXEC CICS
WRITEQ TS QUEUE(WS-TSQ-NAME)
FROM(WS-TSQ-DATA)
LENGTH(WS-TSQ-LEN)
ITEM(WS-TSQ-ITEM)
RESP(WS-RESP)
END-EXEC
IF WS-RESP = DFHRESP(NORMAL)
DISPLAY 'Written item: ' WS-TSQ-ITEM
END-IF
💡 Queue Naming Convention: Always include the terminal ID (EIBTRMID) in the TSQ name. This ensures each user has a private queue. A common pattern is: 3-character prefix + 4-character terminal ID + optional suffix. Example:
BRWT001for a browse queue on terminal T001.
Reading from a TSQ
*--------------------------------------------------------------
* Read a specific item by number
*--------------------------------------------------------------
MOVE 5 TO WS-TSQ-ITEM
EXEC CICS
READQ TS QUEUE(WS-TSQ-NAME)
INTO(WS-TSQ-DATA)
LENGTH(WS-TSQ-LEN)
ITEM(WS-TSQ-ITEM)
RESP(WS-RESP)
END-EXEC
EVALUATE WS-RESP
WHEN DFHRESP(NORMAL)
PERFORM 3000-DISPLAY-ITEM
WHEN DFHRESP(ITEMERR)
MOVE 'Item not found in queue' TO MSGO
WHEN DFHRESP(QIDERR)
MOVE 'Queue does not exist' TO MSGO
WHEN OTHER
PERFORM 9500-LOG-ERROR
END-EVALUATE
Reading Sequentially (NEXT)
*--------------------------------------------------------------
* Read items sequentially
*--------------------------------------------------------------
EXEC CICS
READQ TS QUEUE(WS-TSQ-NAME)
INTO(WS-TSQ-DATA)
LENGTH(WS-TSQ-LEN)
NEXT
RESP(WS-RESP)
END-EXEC
The NEXT option reads the next item in sequence. After the last item, RESP returns ITEMERR.
Updating a TSQ Item
*--------------------------------------------------------------
* Update an existing item (rewrite)
*--------------------------------------------------------------
MOVE 3 TO WS-TSQ-ITEM
EXEC CICS
WRITEQ TS QUEUE(WS-TSQ-NAME)
FROM(WS-TSQ-DATA)
LENGTH(WS-TSQ-LEN)
ITEM(WS-TSQ-ITEM)
REWRITE
RESP(WS-RESP)
END-EXEC
Deleting a TSQ
*--------------------------------------------------------------
* Delete the entire queue when done
*--------------------------------------------------------------
EXEC CICS
DELETEQ TS QUEUE(WS-TSQ-NAME)
RESP(WS-RESP)
END-EXEC
* QIDERR is OK — queue may already be deleted
IF WS-RESP NOT = DFHRESP(NORMAL)
AND WS-RESP NOT = DFHRESP(QIDERR)
PERFORM 9500-LOG-ERROR
END-IF
⚠️ Critical Rule: Always delete your TSQs when the transaction ends (PF3/exit). Orphaned TSQs consume CICS storage indefinitely. A common practice is to also attempt deletion at the start of the transaction (first invocation), in case a previous session terminated abnormally without cleanup.
TSQ Storage Options
| Option | Storage | Survives Region Restart | Use Case |
|---|---|---|---|
| MAIN | Main storage | No | Small, temporary browse data |
| AUXILIARY | Auxiliary storage (DASD) | Yes (if recoverable) | Large data sets, recovery needed |
* Specify auxiliary storage for large queues
EXEC CICS
WRITEQ TS QUEUE(WS-TSQ-NAME)
FROM(WS-TSQ-DATA)
LENGTH(WS-TSQ-LEN)
ITEM(WS-TSQ-ITEM)
AUXILIARY
RESP(WS-RESP)
END-EXEC
TSQ Item Size and Structure
TSQ items are variable-length — each WRITEQ TS can write a different number of bytes. However, for browse/scroll patterns, you typically use a fixed-size item structure for consistency:
01 WS-TSQ-BROWSE-ITEM.
05 WS-BRW-KEY PIC X(10).
05 WS-BRW-NAME PIC X(30).
05 WS-BRW-AMOUNT PIC S9(9)V9(2) COMP-3.
05 WS-BRW-STATUS PIC X(8).
05 WS-BRW-DATE PIC X(10).
01 WS-BRW-LEN PIC S9(4) COMP VALUE 64.
When reading a TSQ item, the LENGTH field is both input and output. On input, it specifies the maximum length your buffer can hold. On output, it contains the actual length of the item read. If the item is longer than your buffer, you receive LENGERR:
* Set max length before reading
MOVE 64 TO WS-BRW-LEN
EXEC CICS
READQ TS QUEUE(WS-TSQ-NAME)
INTO(WS-TSQ-BROWSE-ITEM)
LENGTH(WS-BRW-LEN)
ITEM(WS-ITEM-NUM)
RESP(WS-RESP)
END-EXEC
* After read, WS-BRW-LEN contains actual item length
IF WS-RESP = DFHRESP(LENGERR)
DISPLAY 'Item too large for buffer'
END-IF
TSQ Performance Considerations
Maria Chen tracks these TSQ performance guidelines at GlobalBank:
-
Keep items small. Each TSQ read/write involves CICS storage management. Smaller items mean faster operations. For browse patterns, store only the fields needed for the list display, not the entire database record.
-
Limit queue size. A TSQ with 10,000 items consuming 100 bytes each uses 1 MB of CICS main storage (or auxiliary storage). With 200 concurrent users, that is 200 MB. Use
FETCH FIRST n ROWS ONLYto limit the data loaded. -
Use MAIN for small, short-lived queues. MAIN storage is faster than AUXILIARY because it avoids disk I/O. For browse queues that will exist for a few minutes and contain fewer than 500 items, MAIN is the right choice.
-
Use AUXILIARY for large or long-lived queues. If the queue might grow large or if it needs to survive a CICS warm restart, use AUXILIARY storage.
-
Delete queues promptly. The most common TSQ-related production problem is storage exhaustion from orphaned queues. Delete on exit and at transaction start.
🧪 Try It Yourself: Write a simple CICS program that creates a TSQ with 100 items, reads them by item number, reads them sequentially, updates item 50, and then deletes the queue. Check the RESP code after each operation and display the results. This exercise will make the TSQ API second nature.
30.2 Transient Data Queues (TDQ): Event-Driven Processing
While TSQs are scratch pads for application data, Transient Data Queues are event channels — they feed data to other processes. There are two types:
Intrapartition TDQs
Intrapartition TDQs are internal to CICS. They are commonly used for: - Trigger-level processing: When the queue reaches N items, CICS automatically starts a transaction to process them - Logging: Write audit or debug records that a monitoring transaction collects - Decoupling: Producer transactions write to the queue; consumer transactions read from it
*--------------------------------------------------------------
* Write an audit record to an intrapartition TDQ
*--------------------------------------------------------------
01 WS-AUDIT-RECORD.
05 WS-AUD-TIMESTAMP PIC X(26).
05 WS-AUD-TERM-ID PIC X(4).
05 WS-AUD-TXN-ID PIC X(4).
05 WS-AUD-USER-ID PIC X(8).
05 WS-AUD-ACTION PIC X(10).
05 WS-AUD-DETAIL PIC X(50).
01 WS-AUD-LEN PIC S9(4) COMP VALUE 102.
PROCEDURE DIVISION.
MOVE EIBTRMID TO WS-AUD-TERM-ID
MOVE EIBTRNID TO WS-AUD-TXN-ID
MOVE 'INQUIRY' TO WS-AUD-ACTION
STRING 'Account lookup: ' WS-CA-ACCT-NUM
DELIMITED BY SIZE
INTO WS-AUD-DETAIL
END-STRING
EXEC CICS
WRITEQ TD QUEUE('AUDT')
FROM(WS-AUDIT-RECORD)
LENGTH(WS-AUD-LEN)
RESP(WS-RESP)
END-EXEC
Extrapartition TDQs
Extrapartition TDQs are connected to external data sets (sequential files). They provide a bridge between CICS and the batch world:
*--------------------------------------------------------------
* Write to an extrapartition TDQ (goes to a sequential file)
*--------------------------------------------------------------
EXEC CICS
WRITEQ TD QUEUE('RPTQ')
FROM(WS-REPORT-LINE)
LENGTH(WS-RPT-LEN)
RESP(WS-RESP)
END-EXEC
Reading from a TDQ
Unlike TSQs, TDQ reads are destructive — once read, the item is gone:
EXEC CICS
READQ TD QUEUE('AUDT')
INTO(WS-AUDIT-RECORD)
LENGTH(WS-AUD-LEN)
RESP(WS-RESP)
END-EXEC
IF WS-RESP = DFHRESP(QZERO)
* Queue is empty
DISPLAY 'No more audit records'
END-IF
Trigger-Level Processing
When you define an intrapartition TDQ with a trigger level, CICS automatically starts a specified transaction when the queue reaches that many items:
DEFINE TDQUEUE(AUDT)
TYPE(INTRA)
TRIGGERLEVEL(100)
TRANSID(AUDP)
When 100 audit records accumulate in queue AUDT, CICS starts transaction AUDP to process them. This is a powerful pattern for decoupling real-time transaction processing from batch-like operations.
The trigger-level transaction should drain the queue completely in a single invocation:
*--------------------------------------------------------------
* Trigger-level audit processor (AUDP)
* Started automatically when AUDT queue reaches 100 items
*--------------------------------------------------------------
PROCEDURE DIVISION.
0000-MAIN.
MOVE 0 TO WS-PROCESSED-COUNT
MOVE 0 TO WS-ERROR-COUNT
PERFORM UNTIL WS-RESP = DFHRESP(QZERO)
EXEC CICS
READQ TD QUEUE('AUDT')
INTO(WS-AUDIT-RECORD)
LENGTH(WS-AUD-LEN)
RESP(WS-RESP)
END-EXEC
IF WS-RESP = DFHRESP(NORMAL)
ADD 1 TO WS-PROCESSED-COUNT
PERFORM 1000-WRITE-AUDIT-TO-DB2
END-IF
END-PERFORM
* Log the batch to CSMT
STRING 'AUDP: Processed '
WS-PROCESSED-COUNT ' audit records, '
WS-ERROR-COUNT ' errors'
DELIMITED BY ' '
INTO WS-LOG-MSG
END-STRING
EXEC CICS
WRITEQ TD QUEUE('CSMT')
FROM(WS-LOG-MSG)
LENGTH(WS-LOG-LEN)
END-EXEC
EXEC CICS RETURN END-EXEC.
⚠️ Trigger Level Tuning: Setting the trigger level too low (e.g., 1) causes a transaction start for every single write — high overhead. Setting it too high (e.g., 10,000) means long delays before processing begins. At MedClaim, Tomas Rivera tunes trigger levels based on production volume: 100 for the audit queue (moderate volume), 10 for the alert queue (urgent items), and 500 for the reporting queue (high volume, low urgency).
📊 TSQ vs. TDQ Decision Matrix: | Need | Use | |------|-----| | Browse/scroll data for one user | TSQ (with terminal ID in name) | | Multi-screen state beyond COMMAREA | TSQ | | Audit trail / logging | TDQ (intrapartition) | | Output to a file for batch processing | TDQ (extrapartition) | | Trigger automatic processing at threshold | TDQ (intrapartition with trigger) | | Data that must persist if CICS restarts | TSQ (auxiliary, recoverable) |
30.3 The START Command: Asynchronous Processing
The START command tells CICS to initiate a transaction at some future time or immediately but as a separate task — without waiting for it to complete. This is CICS's mechanism for asynchronous processing.
Immediate Async Start
*--------------------------------------------------------------
* Trigger an async notification after account update
*--------------------------------------------------------------
01 WS-NOTIFY-DATA.
05 WS-NOT-ACCT-NUM PIC X(10).
05 WS-NOT-ACTION PIC X(10).
05 WS-NOT-AMOUNT PIC S9(11)V9(2) COMP-3.
01 WS-NOT-LEN PIC S9(4) COMP VALUE 27.
MOVE WS-CA-ACCT-NUM TO WS-NOT-ACCT-NUM
MOVE 'TRANSFER' TO WS-NOT-ACTION
MOVE WS-TRANSFER-AMT TO WS-NOT-AMOUNT
EXEC CICS
START TRANSID('NTFY')
FROM(WS-NOTIFY-DATA)
LENGTH(WS-NOT-LEN)
RESP(WS-RESP)
END-EXEC
IF WS-RESP = DFHRESP(NORMAL)
MOVE 'Notification queued' TO MSGO
ELSE
MOVE 'Warning: notification failed' TO MSGO
END-IF
The started transaction (NTFY) runs as a completely separate task. It retrieves the data with RETRIEVE:
*--------------------------------------------------------------
* In the NTFY transaction — retrieve data from START
*--------------------------------------------------------------
PROCEDURE DIVISION.
EXEC CICS
RETRIEVE INTO(WS-NOTIFY-DATA)
LENGTH(WS-NOT-LEN)
RESP(WS-RESP)
END-EXEC
IF WS-RESP = DFHRESP(NORMAL)
PERFORM 1000-SEND-NOTIFICATION
END-IF
EXEC CICS RETURN END-EXEC.
Delayed Start (Timer)
*--------------------------------------------------------------
* Start a cleanup transaction in 30 minutes
*--------------------------------------------------------------
EXEC CICS
START TRANSID('CLNP')
FROM(WS-CLEANUP-DATA)
LENGTH(WS-CLNP-LEN)
INTERVAL(003000)
RESP(WS-RESP)
END-EXEC
* INTERVAL(003000) = 00 hours, 30 minutes, 00 seconds
Start at a Specific Time
*--------------------------------------------------------------
* Start end-of-day processing at 5:00 PM
*--------------------------------------------------------------
EXEC CICS
START TRANSID('EODP')
FROM(WS-EOD-DATA)
LENGTH(WS-EOD-LEN)
TIME(170000)
RESP(WS-RESP)
END-EXEC
* TIME(170000) = 17:00:00 (5:00 PM)
💡 Async Pattern: The START command is CICS's equivalent of a message queue or event trigger. Use it when the user's transaction should not wait for a secondary operation to complete — sending emails, generating reports, triggering downstream processing, or scheduling cleanup.
30.4 LINK and XCTL: Program-to-Program Communication
We introduced LINK and XCTL in Chapter 29. Now let us explore them in depth.
LINK with COMMAREA
LINK calls a program and waits for it to return. The COMMAREA is the data exchange mechanism:
*--------------------------------------------------------------
* Call a validation subroutine via LINK
*--------------------------------------------------------------
01 WS-VALIDATION-AREA.
05 WS-VAL-ACCT-NUM PIC X(10).
05 WS-VAL-AMOUNT PIC S9(11)V9(2) COMP-3.
05 WS-VAL-TXN-TYPE PIC X(2).
05 WS-VAL-RETURN-CODE PIC S9(4) COMP.
05 WS-VAL-RETURN-MSG PIC X(50).
01 WS-VAL-LEN PIC S9(4) COMP VALUE 71.
MOVE WS-CA-ACCT-NUM TO WS-VAL-ACCT-NUM
MOVE WS-TXN-AMOUNT TO WS-VAL-AMOUNT
MOVE 'DR' TO WS-VAL-TXN-TYPE
EXEC CICS
LINK PROGRAM('TXNVALID')
COMMAREA(WS-VALIDATION-AREA)
LENGTH(WS-VAL-LEN)
RESP(WS-RESP)
END-EXEC
EVALUATE WS-RESP
WHEN DFHRESP(NORMAL)
EVALUATE WS-VAL-RETURN-CODE
WHEN 0
PERFORM 4000-PROCESS-TXN
WHEN 1
MOVE WS-VAL-RETURN-MSG TO MSGO
PERFORM 1500-SEND-DATAONLY
WHEN OTHER
PERFORM 9500-LOG-ERROR
END-EVALUATE
WHEN DFHRESP(PGMIDERR)
MOVE 'Validation program not found'
TO MSGO
PERFORM 1500-SEND-DATAONLY
WHEN OTHER
PERFORM 9500-LOG-ERROR
END-EVALUATE
XCTL for Menu Navigation
XCTL transfers control permanently — the calling program's storage is freed:
*--------------------------------------------------------------
* Transfer to account maintenance from the menu
*--------------------------------------------------------------
EXEC CICS
XCTL PROGRAM('ACCTMNT')
COMMAREA(WS-MENU-DATA)
LENGTH(WS-MENU-LEN)
RESP(WS-RESP)
END-EXEC
* If we get here, XCTL failed
IF WS-RESP = DFHRESP(PGMIDERR)
MOVE 'Maintenance program not available'
TO MSGO
PERFORM 1500-SEND-DATAONLY
END-IF
When to Use Each
| Scenario | Use | Why |
|---|---|---|
| Call a validation routine | LINK | Need control back to continue processing |
| Call a logging utility | LINK | Need control back |
| Navigate from menu to a functional program | XCTL | No need to return to menu program |
| Chain from one transaction module to another | XCTL | Previous module's work is done |
| Call a stored procedure wrapper | LINK | Need the result |
30.5 Channel/Container Programming: The Modern Approach
COMMAREA has limitations: a maximum practical size of about 32 KB and a flat data structure. Modern CICS introduces channels and containers as a more flexible data exchange mechanism.
A channel is a named collection of containers. Each container holds a named piece of data. Think of it as passing a named dictionary (or map) between programs instead of a single flat buffer.
Creating and Populating Containers
*--------------------------------------------------------------
* Put data into containers on a channel
*--------------------------------------------------------------
01 WS-CHANNEL-NAME PIC X(16) VALUE 'ACCT-CHANNEL'.
01 WS-ACCT-DATA.
05 WS-CN-ACCT-NUM PIC X(10).
05 WS-CN-ACCT-NAME PIC X(50).
05 WS-CN-BALANCE PIC S9(11)V9(2) COMP-3.
01 WS-ACCT-LEN PIC S9(8) COMP VALUE 67.
01 WS-TXN-LIST.
05 WS-CN-TXN OCCURS 10 TIMES.
10 WS-CN-TXN-ID PIC X(15).
10 WS-CN-TXN-AMT PIC S9(11)V9(2) COMP-3.
10 WS-CN-TXN-DATE PIC X(10).
01 WS-TXN-LEN PIC S9(8) COMP VALUE 320.
01 WS-ERROR-MSG PIC X(80).
01 WS-ERR-LEN PIC S9(8) COMP VALUE 80.
PROCEDURE DIVISION.
* Put account data in one container
EXEC CICS
PUT CONTAINER('ACCT-DATA')
CHANNEL(WS-CHANNEL-NAME)
FROM(WS-ACCT-DATA)
FLENGTH(WS-ACCT-LEN)
RESP(WS-RESP)
END-EXEC
* Put transaction list in another container
EXEC CICS
PUT CONTAINER('TXN-LIST')
CHANNEL(WS-CHANNEL-NAME)
FROM(WS-TXN-LIST)
FLENGTH(WS-TXN-LEN)
RESP(WS-RESP)
END-EXEC
* LINK with channel instead of COMMAREA
EXEC CICS
LINK PROGRAM('ACCTPROC')
CHANNEL(WS-CHANNEL-NAME)
RESP(WS-RESP)
END-EXEC
* Read back results
EXEC CICS
GET CONTAINER('ERROR-MSG')
CHANNEL(WS-CHANNEL-NAME)
INTO(WS-ERROR-MSG)
FLENGTH(WS-ERR-LEN)
RESP(WS-RESP)
END-EXEC
IF WS-RESP = DFHRESP(CONTAINERERR)
* Container not found — no error occurred
CONTINUE
END-IF
Advantages of Channels/Containers Over COMMAREA
| Feature | COMMAREA | Channels/Containers |
|---|---|---|
| Maximum size | ~32 KB practical | No practical limit |
| Structure | Flat, single buffer | Named containers, multiple data items |
| Optional data | Must include all fields | Containers can be absent (CONTAINERERR) |
| Type safety | None | Can specify CHAR or BIT data types |
| Web service friendly | Requires manual mapping | Can map directly to JSON/XML |
🔗 Connection to Web Services: Channels and containers are the native data model for CICS web services. When CICS exposes a program as a web service, the JSON or XML request body maps directly to containers. When you design programs using channels/containers from the start, you make them web-service-ready with minimal changes.
Migration Strategy: COMMAREA to Channels
Most existing CICS applications use COMMAREA. Converting to channels/containers is a gradual process. Derek Washington uses this migration approach at GlobalBank:
Phase 1: New programs use channels. Any new LINK or XCTL call uses the channel/container model. Existing programs continue with COMMAREA.
Phase 2: Create wrapper programs. For existing programs that cannot be immediately changed, write thin wrapper programs that accept a channel, extract the container data into a local COMMAREA-format working storage area, and LINK to the original program with that COMMAREA:
*--------------------------------------------------------------
* Wrapper: accepts channel, calls COMMAREA-based program
*--------------------------------------------------------------
PROCEDURE DIVISION.
* Read the channel container
EXEC CICS
GET CONTAINER('REQUEST')
CHANNEL('ACCT-CHANNEL')
INTO(WS-COMMAREA)
FLENGTH(WS-COMM-LEN)
RESP(WS-RESP)
END-EXEC
* Call the legacy program with COMMAREA
EXEC CICS
LINK PROGRAM('OLDPROG')
COMMAREA(WS-COMMAREA)
LENGTH(WS-COMM-LEN)
RESP(WS-RESP)
END-EXEC
* Put the response back in a container
EXEC CICS
PUT CONTAINER('RESPONSE')
CHANNEL('ACCT-CHANNEL')
FROM(WS-COMMAREA)
FLENGTH(WS-COMM-LEN)
END-EXEC
EXEC CICS RETURN END-EXEC.
Phase 3: Convert high-volume programs. Gradually convert the most frequently called programs to accept channels natively, removing the wrapper overhead.
This phased approach avoids a risky "big bang" conversion while moving steadily toward the modern programming model.
The Called Program Reads Containers
*--------------------------------------------------------------
* In the called program — read containers from the channel
*--------------------------------------------------------------
IDENTIFICATION DIVISION.
PROGRAM-ID. ACCTPROC.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-ACCT-DATA.
05 WS-CN-ACCT-NUM PIC X(10).
05 WS-CN-ACCT-NAME PIC X(50).
05 WS-CN-BALANCE PIC S9(11)V9(2) COMP-3.
01 WS-ACCT-LEN PIC S9(8) COMP VALUE 67.
01 WS-RESP PIC S9(8) COMP.
PROCEDURE DIVISION.
* Get the current channel name
EXEC CICS
ASSIGN CHANNEL(WS-CURRENT-CHANNEL)
END-EXEC
* Read account data container
EXEC CICS
GET CONTAINER('ACCT-DATA')
CHANNEL(WS-CURRENT-CHANNEL)
INTO(WS-ACCT-DATA)
FLENGTH(WS-ACCT-LEN)
RESP(WS-RESP)
END-EXEC
IF WS-RESP = DFHRESP(NORMAL)
PERFORM 1000-PROCESS-ACCOUNT
END-IF
* Put result back in a container
EXEC CICS
PUT CONTAINER('RESULT')
CHANNEL(WS-CURRENT-CHANNEL)
FROM(WS-RESULT-DATA)
FLENGTH(WS-RESULT-LEN)
END-EXEC
EXEC CICS RETURN END-EXEC.
30.6 Multi-Screen Transactions: Browse/Scroll Pattern
One of the most common CICS patterns is the browse/scroll screen — displaying a list of records that the user can page through with PF7 (backward) and PF8 (forward). This pattern combines TSQs, COMMAREA state management, and careful cursor positioning.
Architecture of a Browse Transaction
1. User enters search criteria → Query DB2 → Store results in TSQ
2. Display first page (items 1-15) from TSQ
3. User presses PF8 → Display items 16-30 from TSQ
4. User presses PF7 → Display items 1-15 from TSQ
5. User selects a row → Display detail screen
6. User presses PF3 from detail → Return to browse
7. User presses PF3 from browse → Clean up TSQ, exit
COMMAREA for Browse State
01 WS-COMMAREA.
05 WS-CA-STATE PIC X(1).
88 WS-CA-SEARCH VALUE 'S'.
88 WS-CA-BROWSE VALUE 'B'.
88 WS-CA-DETAIL VALUE 'D'.
05 WS-CA-TSQ-NAME PIC X(8).
05 WS-CA-TOTAL-ITEMS PIC S9(4) COMP.
05 WS-CA-TOP-ITEM PIC S9(4) COMP.
05 WS-CA-PAGE-SIZE PIC S9(4) COMP VALUE 15.
05 WS-CA-SEARCH-KEY PIC X(10).
05 WS-CA-SELECTED-KEY PIC X(10).
01 WS-COMMAREA-LEN PIC S9(4) COMP VALUE 30.
Loading Data into the TSQ
2000-LOAD-BROWSE-DATA.
*--------------------------------------------------------------
* Query DB2 and store results in a TSQ
*--------------------------------------------------------------
* Delete any existing queue from previous session
EXEC CICS
DELETEQ TS QUEUE(WS-CA-TSQ-NAME)
RESP(WS-RESP)
END-EXEC
* Open cursor for search results
EXEC SQL
DECLARE BROWSE-CURSOR CURSOR FOR
SELECT ACCT_NUMBER, ACCT_NAME,
ACCT_BALANCE, ACCT_STATUS
FROM ACCOUNT
WHERE BRANCH_CODE = :WS-CA-SEARCH-KEY
ORDER BY ACCT_NAME
FETCH FIRST 500 ROWS ONLY
END-EXEC
EXEC SQL OPEN BROWSE-CURSOR END-EXEC
MOVE 0 TO WS-CA-TOTAL-ITEMS
PERFORM UNTIL SQLCODE = +100
EXEC SQL
FETCH BROWSE-CURSOR
INTO :WS-TSQ-ACCT-NUM,
:WS-TSQ-ACCT-NAME,
:WS-TSQ-BALANCE,
:WS-TSQ-STATUS
END-EXEC
IF SQLCODE = 0
ADD 1 TO WS-CA-TOTAL-ITEMS
EXEC CICS
WRITEQ TS QUEUE(WS-CA-TSQ-NAME)
FROM(WS-TSQ-DATA)
LENGTH(WS-TSQ-LEN)
ITEM(WS-TSQ-ITEM)
MAIN
RESP(WS-RESP)
END-EXEC
END-IF
END-PERFORM
EXEC SQL CLOSE BROWSE-CURSOR END-EXEC
IF WS-CA-TOTAL-ITEMS = 0
MOVE 'No accounts found for this branch'
TO MSGO
PERFORM 1500-SEND-SEARCH-DATAONLY
ELSE
MOVE 1 TO WS-CA-TOP-ITEM
PERFORM 3000-DISPLAY-BROWSE-PAGE
END-IF.
Displaying a Page from the TSQ
3000-DISPLAY-BROWSE-PAGE.
*--------------------------------------------------------------
* Display one page of browse data from the TSQ
*--------------------------------------------------------------
INITIALIZE ACCTBRWO
* Calculate page bounds
COMPUTE WS-LAST-ITEM =
WS-CA-TOP-ITEM + WS-CA-PAGE-SIZE - 1
IF WS-LAST-ITEM > WS-CA-TOTAL-ITEMS
MOVE WS-CA-TOTAL-ITEMS TO WS-LAST-ITEM
END-IF
* Read items from TSQ and populate map rows
MOVE 0 TO WS-ROW-IDX
PERFORM VARYING WS-TSQ-ITEM
FROM WS-CA-TOP-ITEM BY 1
UNTIL WS-TSQ-ITEM > WS-LAST-ITEM
EXEC CICS
READQ TS QUEUE(WS-CA-TSQ-NAME)
INTO(WS-TSQ-DATA)
LENGTH(WS-TSQ-LEN)
ITEM(WS-TSQ-ITEM)
RESP(WS-RESP)
END-EXEC
IF WS-RESP = DFHRESP(NORMAL)
ADD 1 TO WS-ROW-IDX
MOVE WS-TSQ-ACCT-NUM
TO WS-BRW-ACCT(WS-ROW-IDX)
MOVE WS-TSQ-ACCT-NAME
TO WS-BRW-NAME(WS-ROW-IDX)
MOVE WS-TSQ-BALANCE
TO WS-BRW-BAL-FMT
MOVE WS-BRW-BAL-FMT
TO WS-BRW-BAL(WS-ROW-IDX)
MOVE WS-TSQ-STATUS
TO WS-BRW-STAT(WS-ROW-IDX)
END-IF
END-PERFORM
* Set status message
STRING 'Items '
WS-CA-TOP-ITEM '-' WS-LAST-ITEM
' of ' WS-CA-TOTAL-ITEMS
' PF7=Back PF8=Forward PF3=Exit'
DELIMITED BY ' '
INTO MSGO
END-STRING
MOVE 'B' TO WS-CA-STATE
PERFORM 1500-SEND-BROWSE-DATAONLY.
Handling Scroll Keys
4000-PROCESS-BROWSE-INPUT.
*--------------------------------------------------------------
* Handle PF7/PF8 scrolling and row selection
*--------------------------------------------------------------
EVALUATE EIBAID
WHEN DFHPF7
* Scroll backward
COMPUTE WS-CA-TOP-ITEM =
WS-CA-TOP-ITEM - WS-CA-PAGE-SIZE
IF WS-CA-TOP-ITEM < 1
MOVE 1 TO WS-CA-TOP-ITEM
MOVE 'Already at top of list' TO MSGO
END-IF
PERFORM 3000-DISPLAY-BROWSE-PAGE
WHEN DFHPF8
* Scroll forward
COMPUTE WS-NEW-TOP =
WS-CA-TOP-ITEM + WS-CA-PAGE-SIZE
IF WS-NEW-TOP > WS-CA-TOTAL-ITEMS
MOVE 'Already at end of list' TO MSGO
PERFORM 3000-DISPLAY-BROWSE-PAGE
ELSE
MOVE WS-NEW-TOP TO WS-CA-TOP-ITEM
PERFORM 3000-DISPLAY-BROWSE-PAGE
END-IF
WHEN DFHENTER
* User selected a row — check selection field
PERFORM 4100-PROCESS-SELECTION
WHEN DFHPF3
* Exit — clean up TSQ
EXEC CICS
DELETEQ TS QUEUE(WS-CA-TSQ-NAME)
RESP(WS-RESP)
END-EXEC
EXEC CICS RETURN END-EXEC
WHEN OTHER
MOVE 'PF7=Back PF8=Fwd Enter=Select PF3=Exit'
TO MSGO
PERFORM 3000-DISPLAY-BROWSE-PAGE
END-EVALUATE.
🧪 Try It Yourself: Design a BMS map for the browse screen with 15 repeating rows. Each row should have: a 1-character selection field (UNPROT), account number, account name, balance, and status. The selection field is where the user types 'S' to select a record.
Processing Row Selections
When the user types 'S' next to a row and presses Enter, the program needs to determine which row was selected. This requires scanning the selection fields in the received map:
4100-PROCESS-SELECTION.
*--------------------------------------------------------------
* Scan selection fields for 'S' and show detail
*--------------------------------------------------------------
EXEC CICS
RECEIVE MAP('ACCTBRW')
MAPSET('BRWSET')
INTO(ACCTBRWI)
RESP(WS-RESP)
END-EXEC
IF WS-RESP = DFHRESP(MAPFAIL)
MOVE 'Type S next to a row, press Enter'
TO MSGO
PERFORM 3000-DISPLAY-BROWSE-PAGE
EXIT PARAGRAPH
END-IF
* Scan through the 15 selection fields
MOVE 0 TO WS-SELECTED-ROW
PERFORM VARYING WS-SCAN-IDX FROM 1 BY 1
UNTIL WS-SCAN-IDX > 15
OR WS-SELECTED-ROW > 0
IF BRW-SELL(WS-SCAN-IDX) > 0
IF BRW-SELI(WS-SCAN-IDX) = 'S'
OR BRW-SELI(WS-SCAN-IDX) = 's'
MOVE WS-SCAN-IDX TO WS-SELECTED-ROW
ELSE
* Invalid selection character
MOVE 'Type S to select (not '
& BRW-SELI(WS-SCAN-IDX)
& ')' TO MSGO
PERFORM 3000-DISPLAY-BROWSE-PAGE
EXIT PARAGRAPH
END-IF
END-IF
END-PERFORM
IF WS-SELECTED-ROW = 0
MOVE 'No row selected. Type S to select.'
TO MSGO
PERFORM 3000-DISPLAY-BROWSE-PAGE
ELSE
* Calculate TSQ item number
COMPUTE WS-SELECTED-ITEM =
WS-CA-TOP-ITEM + WS-SELECTED-ROW - 1
PERFORM 5000-SHOW-DETAIL
END-IF.
This pattern — scanning repeating fields for a selection marker — is one of the most common patterns in production CICS programs. The key points:
- Accept both uppercase and lowercase ('S' and 's') — users may have Caps Lock off
- Reject invalid selection characters rather than ignoring them — provide feedback
- Handle the case where no selection is found — the user pressed Enter without typing 'S' anywhere
- Calculate the TSQ item number from the screen row position: item = top-item + selected-row - 1
The Detail-to-Browse Return Pattern
When the user views a detail record and presses PF3 to return to the browse, the browse page must redisplay at the same position it was in before the detail was shown. This is why the COMMAREA stores WS-CA-TOP-ITEM — the browse redisplay reads from the TSQ starting at the saved position, giving the user a seamless experience:
6000-RETURN-TO-BROWSE.
* State returns to 'B' (browse)
* WS-CA-TOP-ITEM is unchanged from when we left
MOVE 'B' TO WS-CA-STATE
PERFORM 3000-DISPLAY-BROWSE-PAGE
* User sees the same page they were on before
If the TSQ has been deleted (perhaps by a cleanup transaction while the user was viewing the detail), handle the error gracefully by re-executing the search:
* Check if TSQ still exists
EXEC CICS
READQ TS QUEUE(WS-CA-TSQ-NAME)
INTO(WS-TSQ-DATA)
LENGTH(WS-TSQ-LEN)
ITEM(1)
RESP(WS-RESP)
END-EXEC
IF WS-RESP = DFHRESP(QIDERR)
* TSQ was cleaned up — reload
MOVE 'Data expired. Reloading...' TO MSGO
PERFORM 2000-LOAD-BROWSE-DATA
ELSE
PERFORM 3000-DISPLAY-BROWSE-PAGE
END-IF
Browse Map Design with Repeating Groups
The BMS map for a browse screen uses a repeating pattern of fields. Here is a partial example showing three of the fifteen rows:
* --- Row 5: Column Headers ---
DFHMDF POS=(5,2), X
LENGTH=60, X
ATTRB=(ASKIP,NORM), X
INITIAL='S Account Name X
Balance Status'
*
* --- Row 6: First data row ---
SEL01 DFHMDF POS=(6,2), X
LENGTH=1, X
ATTRB=(UNPROT,NORM,IC)
DFHMDF POS=(6,4),LENGTH=1,ATTRB=ASKIP
ACCT01 DFHMDF POS=(6,5), X
LENGTH=10, X
ATTRB=(ASKIP,BRT)
NAME01 DFHMDF POS=(6,16), X
LENGTH=25, X
ATTRB=(ASKIP,BRT)
BAL01 DFHMDF POS=(6,42), X
LENGTH=13, X
ATTRB=(ASKIP,BRT)
STAT01 DFHMDF POS=(6,56), X
LENGTH=8, X
ATTRB=(ASKIP,BRT)
*
* --- Row 7: Second data row (same pattern) ---
SEL02 DFHMDF POS=(7,2),LENGTH=1,ATTRB=(UNPROT,NORM)
DFHMDF POS=(7,4),LENGTH=1,ATTRB=ASKIP
ACCT02 DFHMDF POS=(7,5),LENGTH=10,ATTRB=(ASKIP,BRT)
NAME02 DFHMDF POS=(7,16),LENGTH=25,ATTRB=(ASKIP,BRT)
BAL02 DFHMDF POS=(7,42),LENGTH=13,ATTRB=(ASKIP,BRT)
STAT02 DFHMDF POS=(7,56),LENGTH=8,ATTRB=(ASKIP,BRT)
The generated symbolic map creates arrays for the repeating fields, which is why we can use subscripts like BRW-SELI(WS-SCAN-IDX) to iterate through the rows programmatically.
📊 Page Size Trade-offs: A larger page size (20 rows) means fewer PF8 presses but a denser screen. A smaller page size (10 rows) means more white space but more scrolling. The standard 3270 screen has 24 rows; after title (row 1), column headers (row 5), message line (row 22), and function keys (row 23-24), you have approximately 15 usable rows for data. This is why 15 rows per page is the most common browse page size.
30.7 HANDLE ABEND: Error Recovery in CICS
When a CICS task encounters a fatal error (division by zero, invalid address, DB2 timeout), CICS raises an abend. Without explicit handling, the task terminates and the user sees an error message. The HANDLE ABEND command lets you intercept abends and take recovery action.
Setting Up Abend Handling
PROCEDURE DIVISION.
0000-MAIN.
*--------------------------------------------------------------
* Establish abend handler first
*--------------------------------------------------------------
EXEC CICS
HANDLE ABEND PROGRAM('ABERRHDL')
END-EXEC
* Or handle within the same program:
EXEC CICS
HANDLE ABEND LABEL(9900-ABEND-HANDLER)
END-EXEC
* ... normal processing ...
9900-ABEND-HANDLER.
*--------------------------------------------------------------
* Abend recovery — clean up and notify user
*--------------------------------------------------------------
* Log the error
STRING 'ABEND in ' EIBTRNID
' at term ' EIBTRMID
' task ' EIBTASKN
DELIMITED BY SIZE
INTO WS-LOG-MSG
END-STRING
EXEC CICS
WRITEQ TD QUEUE('CSMT')
FROM(WS-LOG-MSG)
LENGTH(WS-LOG-LEN)
RESP(WS-RESP)
END-EXEC
* Clean up TSQ if it exists
EXEC CICS
DELETEQ TS QUEUE(WS-CA-TSQ-NAME)
RESP(WS-RESP)
END-EXEC
* Notify the user
MOVE 'A system error occurred. Please try again.'
TO MSGO
EXEC CICS
SEND MAP('ERRMSG')
MAPSET('ERRSET')
FROM(ERRMSGO)
ERASE
RESP(WS-RESP)
END-EXEC
EXEC CICS RETURN END-EXEC.
HANDLE ABEND Scope
The HANDLE ABEND command is scoped to the current program level. If program A LINKs to program B, and program B abends, program B's HANDLE ABEND (if any) gets control first. If program B does not have a HANDLE ABEND, the abend propagates up to program A's handler. This stack-like behavior is consistent with the LINK/RETURN program model. Note that XCTL does not preserve the calling program's abend handler — since XCTL replaces the current program, the old handler is lost.
You can also temporarily disable abend handling and re-enable it:
* Disable abend handling (let CICS handle it)
EXEC CICS HANDLE ABEND CANCEL END-EXEC
* Re-enable with the original handler
EXEC CICS HANDLE ABEND LABEL(9900-ABEND-HANDLER)
END-EXEC
Programmatic ABEND
Sometimes you need to intentionally abend a task — for example, when a critical data integrity condition is violated:
* Intentional abend with a meaningful code
EXEC CICS ABEND ABCODE('DINT') END-EXEC
* DINT = Data INTegrity violation
Abend codes are 4 characters. Use a consistent naming scheme so operations staff can quickly identify the source and nature of the problem.
⚠️ Abend Code Conventions: Codes starting with 'A' are reserved by CICS (e.g., ASRA = program check, AEY7 = security violation). Use codes starting with a letter that identifies your application: 'G' for GlobalBank, 'M' for MedClaim, etc.
Understanding Common CICS Abend Codes
Every CICS developer must recognize these abend codes on sight — they are the most common causes of production incidents:
| Code | Name | Cause | Action |
|---|---|---|---|
| ASRA | Program Check | Division by zero, subscript out of range, invalid address | Check COBOL program logic, verify array bounds, check COMMAREA access when EIBCALEN=0 |
| AICA | Runaway Task | Program in an infinite loop; CICS purges it after ICVR seconds | Check PERFORM loops for missing termination conditions |
| AKCT | Task Purge | Task exceeded its transaction time limit | Optimize the program or increase DTIMOUT for the transaction |
| AEI0 | Program Not Found | LINK or XCTL to a program that is not defined or not available | Check program name spelling, verify CSD definition, check NEWCOPY status |
| AEI9 | Map Not Found | SEND MAP with a map or mapset that is not installed | Verify mapset is defined and installed in CICS |
| AEY7 | Security Check | User not authorized for the resource | Check RACF definitions for the user and resource |
| AEIO | I/O Error | Unrecoverable I/O error on a file or queue | Check file status, verify VSAM dataset health |
Transaction Dump Analysis
When a CICS program abends without a HANDLE ABEND, CICS produces a transaction dump. The dump contains: - The program's storage at the time of the abend - The EIB contents - The COMMAREA contents - The register contents (for determining the failing instruction)
The dump is written to the CICS dump dataset and can be analyzed with the IPCS (Interactive Problem Control System) utility or CICS Explorer. The most critical piece of information is the offset of the failing instruction, which you can map to a source line using the compiler listing.
ASRA ABEND at offset X'0002B4' in program ACCTINQ
To find the failing line: 1. Open the compiler listing for ACCTINQ 2. Locate the Procedure Division Map section 3. Find offset 2B4 (hex) in the generated code 4. Map it back to the COBOL source line number
💡 Debugging Tip: Always compile with
LISTandMAPoptions so that the compiler listing includes the Procedure Division Map. Without this map, translating an abend offset to a source line is extremely difficult. Production compilations should always include these options.
30.8 CICS Web Services: Bridging Old and New
Modern CICS allows your COBOL programs to serve as web service providers (receiving HTTP requests) or consumers (making HTTP requests). This is the primary mechanism for integrating mainframe COBOL with modern architectures.
CICS as a Web Service Provider
Using CICS's built-in pipeline infrastructure, a COBOL program can receive JSON requests and return JSON responses:
*--------------------------------------------------------------
* COBOL program exposed as a REST service
* URL: /globalbank/api/v1/accounts/{acctNum}
* Method: GET
* Response: JSON with account details
*--------------------------------------------------------------
PROCEDURE DIVISION.
* CICS receives the HTTP request and puts data in containers
* Read the account number from the request
EXEC CICS
GET CONTAINER('DFHWS-URI-PATHINFO')
CHANNEL('DFHWS-DATA')
INTO(WS-URI-PATH)
FLENGTH(WS-PATH-LEN)
RESP(WS-RESP)
END-EXEC
* Extract account number from path
PERFORM 1000-PARSE-ACCT-FROM-PATH
* Look up account (same DB2 logic as terminal program)
PERFORM 2000-LOOKUP-ACCOUNT
* Build JSON response
STRING '{"accountNumber":"' WS-ACCT-NUM '",'
'"accountName":"' WS-ACCT-NAME '",'
'"balance":' WS-BALANCE-STR ','
'"status":"' WS-STATUS-DESC '"}'
DELIMITED BY ' '
INTO WS-JSON-RESPONSE
END-STRING
* Put response in container
EXEC CICS
PUT CONTAINER('DFHWS-BODY')
CHANNEL('DFHWS-DATA')
FROM(WS-JSON-RESPONSE)
FLENGTH(WS-JSON-LEN)
CHAR
END-EXEC
EXEC CICS RETURN END-EXEC.
CICS as a Web Service Consumer
*--------------------------------------------------------------
* Call an external REST API from CICS
*--------------------------------------------------------------
01 WS-URL PIC X(200).
01 WS-URL-LEN PIC S9(8) COMP.
01 WS-HTTP-RESP PIC X(2000).
01 WS-HTTP-RESP-LEN PIC S9(8) COMP VALUE 2000.
01 WS-HTTP-STATUS PIC S9(8) COMP.
01 WS-SESS-TOKEN PIC X(8).
PROCEDURE DIVISION.
MOVE 'https://api.creditcheck.com/v1/score/'
TO WS-URL
STRING WS-URL WS-CUSTOMER-ID
DELIMITED BY SPACES
INTO WS-URL
END-STRING
MOVE FUNCTION LENGTH(FUNCTION TRIM(WS-URL))
TO WS-URL-LEN
* Open an HTTP connection
EXEC CICS WEB OPEN
HOST('api.creditcheck.com')
PORTNUMBER(443)
SCHEME(HTTPS)
SESSTOKEN(WS-SESS-TOKEN)
RESP(WS-RESP)
END-EXEC
* Send the request
EXEC CICS WEB SEND
SESSTOKEN(WS-SESS-TOKEN)
GET
PATH(WS-URL)
PATHLENGTH(WS-URL-LEN)
RESP(WS-RESP)
END-EXEC
* Receive the response
EXEC CICS WEB RECEIVE
SESSTOKEN(WS-SESS-TOKEN)
INTO(WS-HTTP-RESP)
LENGTH(WS-HTTP-RESP-LEN)
STATUSCODE(WS-HTTP-STATUS)
RESP(WS-RESP)
END-EXEC
* Close the connection
EXEC CICS WEB CLOSE
SESSTOKEN(WS-SESS-TOKEN)
RESP(WS-RESP)
END-EXEC
IF WS-HTTP-STATUS = 200
PERFORM 5000-PARSE-CREDIT-RESPONSE
ELSE
MOVE 'Credit check unavailable' TO MSGO
END-IF.
⚖️ The Modernization Spectrum: Web service integration represents the sweet spot on the modernization spectrum. The core COBOL business logic remains unchanged and battle-tested. Only the interface layer is modernized. This approach delivers immediate value (mobile access, partner integration, cloud connectivity) with minimal risk to the underlying systems. Maria Chen calls it "evolution, not revolution."
JSON Transformation with CICS JSON Assist
Building JSON strings with STRING statements (as shown above) works for simple responses, but it is fragile and error-prone for complex data structures. CICS TS V5.2+ provides JSON Assist, which automatically generates transformation code between COBOL copybooks and JSON.
The process works as follows:
- Define a COBOL copybook with your data structure
- Run the DFHJS2LS utility (JSON Schema to Language Structure) to generate a JSON binding file
- Deploy the binding as a BUNDLE resource in CICS
- CICS handles the transformation automatically — incoming JSON is parsed into COBOL fields, and outgoing COBOL fields are serialized to JSON
*--------------------------------------------------------------
* Copybook: ACCTRESP — defines the JSON response structure
* DFHJS2LS generates the binding automatically
*--------------------------------------------------------------
01 ACCT-RESPONSE.
05 ACCT-NUM PIC X(10).
05 ACCT-NAME PIC X(50).
05 ACCT-TYPE PIC X(2).
05 ACCT-BALANCE PIC S9(11)V9(2) COMP-3.
05 ACCT-STATUS PIC X(1).
05 BRANCH-CODE PIC X(4).
05 LAST-ACTIVITY PIC X(26).
With JSON Assist, the COBOL program simply populates the copybook fields. CICS converts them to JSON automatically:
{
"accountNumber": "1234567890",
"accountName": "GlobalBank Checking",
"accountType": "CH",
"accountBalance": 15234.50,
"accountStatus": "A",
"branchCode": "0042",
"lastActivity": "2024-11-15-10.30.45.123456"
}
This eliminates the tedious and error-prone STRING-based JSON construction.
Pipeline Configuration
CICS web services use a pipeline — a series of processing steps that handle message transformation, security, and routing. The pipeline is defined in an XML configuration file:
<!-- Provider pipeline: incoming HTTP to COBOL -->
<provider_pipeline>
<transport>
<default_transport_handler/>
</transport>
<service>
<service_handler_list>
<cics_soap_1.1_handler/>
</service_handler_list>
<terminal_handler>
<cics_json_handler/>
</terminal_handler>
</service>
</provider_pipeline>
Maria Chen notes that pipeline configuration is typically handled by the CICS systems programmer, not the application developer. The COBOL developer's responsibility is to write clean copybooks that map naturally to JSON structures and to populate the response fields correctly.
HTTP Status Code Handling
When consuming external web services, robust status code handling is essential. A production program should handle at least these scenarios:
*--------------------------------------------------------------
* Evaluate HTTP response status
*--------------------------------------------------------------
EVALUATE TRUE
WHEN WS-HTTP-STATUS = 200
* Success — parse the response
PERFORM 5000-PARSE-RESPONSE
WHEN WS-HTTP-STATUS = 401
OR WS-HTTP-STATUS = 403
* Authentication/authorization failure
MOVE 'External service: auth failed'
TO WS-LOG-MSG
PERFORM 8000-WRITE-LOG
MOVE 'Service unavailable (auth)' TO MSGO
WHEN WS-HTTP-STATUS = 404
* Resource not found
MOVE 'Record not found at service' TO MSGO
WHEN WS-HTTP-STATUS = 429
* Rate limited — try again later
MOVE 'Service busy. Try again.' TO MSGO
WHEN WS-HTTP-STATUS >= 500
* Server error — not our fault
MOVE 'External service error' TO MSGO
STRING 'HTTP ' WS-HTTP-STATUS
' from creditcheck API'
DELIMITED BY SIZE
INTO WS-LOG-MSG
END-STRING
PERFORM 8000-WRITE-LOG
WHEN OTHER
STRING 'Unexpected HTTP status: '
WS-HTTP-STATUS
DELIMITED BY SIZE
INTO WS-LOG-MSG
END-STRING
PERFORM 8000-WRITE-LOG
MOVE 'Service returned unexpected status'
TO MSGO
END-EVALUATE
🔗 Connection Pooling: CICS manages HTTP connections internally. For frequently called services, configure a URIMAP resource that enables connection pooling — CICS reuses TCP connections instead of opening and closing them for each request. This dramatically improves throughput when a CICS transaction makes hundreds of external API calls per second.
30.9 CICS Security: RACF Integration
CICS integrates with RACF (Resource Access Control Facility) — IBM's security product for z/OS — to control who can execute transactions, access files, and invoke programs. Security in CICS operates at multiple levels, and understanding these layers is essential for building applications that protect sensitive data while allowing authorized users to do their work.
Security Layers in CICS
CICS security is enforced through resource-level checking. Each type of resource has its own RACF class:
| Resource Type | RACF Class | Example |
|---|---|---|
| Transactions | CICSTRN | Can user execute transaction AMNT? |
| Programs | CICSPROG | Can user invoke program ACCTUPD? |
| Files | CICSFILE | Can user read/write to ACCTFILE? |
| TSQs | CICSTQS | Can user access the TSQ? |
| TDQs | CICSTDQ | Can user write to queue AUDT? |
| DB2 resources | DB2 own classes | Managed separately through DB2 authorization |
The CICS system programmer defines which resources are protected and at what level. The application programmer's job is to handle authorization failures gracefully rather than letting the user see cryptic CICS error messages.
Checking User Authorization
*--------------------------------------------------------------
* Check if the current user is authorized for the operation
*--------------------------------------------------------------
EXEC CICS
QUERY SECURITY
RESTYPE('TRANSATTACH')
RESID('AMNT')
RESIDLENGTH(4)
LOGMESSAGE(LOG)
RESP(WS-RESP)
END-EXEC
EVALUATE WS-RESP
WHEN DFHRESP(NORMAL)
* User is authorized
CONTINUE
WHEN DFHRESP(NOTAUTH)
* User is NOT authorized
MOVE 'You are not authorized for this function'
TO MSGO
PERFORM 1500-SEND-DATAONLY
EXIT PARAGRAPH
WHEN OTHER
PERFORM 9500-LOG-ERROR
END-EVALUATE
At GlobalBank, Derek Washington checks authorization before displaying the edit screen for account maintenance. This prevents unauthorized users from even seeing editable fields — a principle known as security at the earliest point. If a user is not authorized, they see the account in read-only mode instead.
Role-Based Screen Behavior
A common pattern is to display different screen layouts based on the user's authorization level:
*--------------------------------------------------------------
* Check authorization and set screen mode
*--------------------------------------------------------------
EXEC CICS
QUERY SECURITY
RESTYPE('TRANSATTACH')
RESID('AMNT')
RESIDLENGTH(4)
LOGMESSAGE(NOLOG)
RESP(WS-RESP)
END-EXEC
IF WS-RESP = DFHRESP(NORMAL)
* Authorized for maintenance — show editable fields
MOVE DFHBMPRT TO ANAMEMNTA
MOVE DFHBMPRT TO ATYPEMNTA
MOVE 'PF5=Save PF3=Exit' TO MSGO
ELSE
* Read-only access — protect all fields
MOVE DFHBMASK TO ANAMEMNTA
MOVE DFHBMASK TO ATYPEMNTA
MOVE 'View Only PF3=Exit' TO MSGO
END-IF
This approach uses a single program and a single BMS map for both read-only and edit modes. The attribute bytes are set dynamically based on the user's authorization. Users who lack edit permission see the same data but cannot modify it.
Getting User Information
*--------------------------------------------------------------
* Get the current user's ID from CICS
*--------------------------------------------------------------
01 WS-USER-ID PIC X(8).
EXEC CICS
ASSIGN USERID(WS-USER-ID)
END-EXEC
DISPLAY 'Current user: ' WS-USER-ID
30.10 GlobalBank Scenario: Multi-Screen Account Maintenance
Derek Washington is building GlobalBank's account maintenance transaction (AMNT) — a multi-screen application that allows authorized staff to update account information. The transaction has three screens:
- Search Screen: Enter account number or search by name
- Display/Edit Screen: Show account details with editable fields
- Confirmation Screen: Review changes before saving
The State Machine
┌─────────┐ Enter ┌──────────┐ Enter ┌─────────────┐
│ SEARCH │─────────▶│ DISPLAY/ │─────────▶│ CONFIRMATION│
│ Screen │ │ EDIT │ │ Screen │
│ State=S │◀─────────│ State=E │◀─────────│ State=C │
└────┬────┘ PF3 └────┬─────┘ PF3 └──────┬──────┘
│ │ │
│ PF3 │ PF5=Save │ Enter=Commit
│ │ (→ Confirm) │ PF3=Cancel
▼ │ │ (→ Edit)
EXIT │ │
▼ ▼
DB2 UPDATE DB2 COMMIT
The COMMAREA Design
01 WS-COMMAREA.
05 WS-CA-STATE PIC X(1).
88 WS-CA-SEARCH VALUE 'S'.
88 WS-CA-EDIT VALUE 'E'.
88 WS-CA-CONFIRM VALUE 'C'.
05 WS-CA-ACCT-NUM PIC X(10).
05 WS-CA-TSQ-NAME PIC X(8).
* Original values for change detection
05 WS-CA-ORIG-NAME PIC X(50).
05 WS-CA-ORIG-TYPE PIC X(2).
05 WS-CA-ORIG-STATUS PIC X(1).
05 WS-CA-ORIG-BRANCH PIC X(4).
* Modified values pending confirmation
05 WS-CA-NEW-NAME PIC X(50).
05 WS-CA-NEW-TYPE PIC X(2).
05 WS-CA-NEW-STATUS PIC X(1).
05 WS-CA-NEW-BRANCH PIC X(4).
01 WS-COMMAREA-LEN PIC S9(4) COMP VALUE 133.
The Edit Screen: Detecting Changes
5000-PROCESS-EDIT-INPUT.
*--------------------------------------------------------------
* Receive edited fields and detect changes
*--------------------------------------------------------------
EXEC CICS
RECEIVE MAP('ACCTMNT')
MAPSET('MNTSET')
INTO(ACCTMNTI)
RESP(WS-RESP)
END-EXEC
IF WS-RESP = DFHRESP(MAPFAIL)
MOVE 'No changes detected' TO MSGO
PERFORM 1500-SEND-EDIT-DATAONLY
EXIT PARAGRAPH
END-IF
* Check which fields were modified (length > 0)
MOVE 'N' TO WS-CHANGES-FOUND
IF ANAMEMNTL > 0
MOVE ANAMEMNTI TO WS-CA-NEW-NAME
IF WS-CA-NEW-NAME NOT = WS-CA-ORIG-NAME
MOVE 'Y' TO WS-CHANGES-FOUND
END-IF
ELSE
MOVE WS-CA-ORIG-NAME TO WS-CA-NEW-NAME
END-IF
IF ATYPEMNTL > 0
MOVE ATYPEMNTI TO WS-CA-NEW-TYPE
PERFORM 5100-VALIDATE-ACCT-TYPE
IF WS-CA-NEW-TYPE NOT = WS-CA-ORIG-TYPE
MOVE 'Y' TO WS-CHANGES-FOUND
END-IF
ELSE
MOVE WS-CA-ORIG-TYPE TO WS-CA-NEW-TYPE
END-IF
IF WS-CHANGES-FOUND = 'N'
MOVE 'No changes to save' TO MSGO
PERFORM 1500-SEND-EDIT-DATAONLY
ELSE
PERFORM 6000-SHOW-CONFIRMATION
END-IF.
The Confirmation Screen: Showing Before/After
6000-SHOW-CONFIRMATION.
*--------------------------------------------------------------
* Display original and new values for review
*--------------------------------------------------------------
INITIALIZE ACCTCNFO
MOVE WS-CA-ACCT-NUM TO CONFACCTO
* Show changed fields with before/after
IF WS-CA-NEW-NAME NOT = WS-CA-ORIG-NAME
MOVE WS-CA-ORIG-NAME TO CONFNAMBO
MOVE WS-CA-NEW-NAME TO CONFNAMAO
MOVE '*' TO CONFNAMCO
ELSE
MOVE WS-CA-ORIG-NAME TO CONFNAMBO
MOVE '(no change)' TO CONFNAMAO
END-IF
IF WS-CA-NEW-TYPE NOT = WS-CA-ORIG-TYPE
MOVE WS-CA-ORIG-TYPE TO CONFTYPBO
MOVE WS-CA-NEW-TYPE TO CONFTYPAO
MOVE '*' TO CONFTYPC0
ELSE
MOVE WS-CA-ORIG-TYPE TO CONFTYPBO
MOVE '(no change)' TO CONFTYPAO
END-IF
MOVE 'Enter=Confirm PF3=Cancel' TO CONFMSGO
MOVE 'C' TO WS-CA-STATE
EXEC CICS
SEND MAP('ACCTCNF')
MAPSET('MNTSET')
FROM(ACCTCNFO)
ERASE
RESP(WS-RESP)
END-EXEC
EXEC CICS
RETURN TRANSID('AMNT')
COMMAREA(WS-COMMAREA)
LENGTH(WS-COMMAREA-LEN)
END-EXEC.
The Commit: Optimistic Locking
7000-COMMIT-CHANGES.
*--------------------------------------------------------------
* Apply changes with optimistic locking
* Check that original values haven't changed since we read them
*--------------------------------------------------------------
EXEC SQL
UPDATE ACCOUNT
SET ACCT_NAME = :WS-CA-NEW-NAME,
ACCT_TYPE = :WS-CA-NEW-TYPE,
ACCT_STATUS = :WS-CA-NEW-STATUS,
BRANCH_CODE = :WS-CA-NEW-BRANCH,
LAST_ACTIVITY = CURRENT TIMESTAMP
WHERE ACCT_NUMBER = :WS-CA-ACCT-NUM
AND ACCT_NAME = :WS-CA-ORIG-NAME
AND ACCT_TYPE = :WS-CA-ORIG-TYPE
AND ACCT_STATUS = :WS-CA-ORIG-STATUS
AND BRANCH_CODE = :WS-CA-ORIG-BRANCH
END-EXEC
EVALUATE SQLCODE
WHEN 0
IF SQLERRD(3) = 1
MOVE 'Changes saved successfully' TO MSGO
ELSE
* Another user changed the record
MOVE 'Record was modified by another '
& 'user. Please retry.' TO MSGO
END-IF
WHEN -911
MOVE 'Record locked. Please try again.'
TO MSGO
WHEN OTHER
STRING 'DB2 error: ' SQLCODE
DELIMITED BY SIZE INTO MSGO
END-STRING
END-EVALUATE.
💡 Optimistic Locking: Notice the WHERE clause includes the original values of all changeable fields. This is optimistic locking — we assume no one else will change the record while we are editing it. If someone does, the UPDATE affects 0 rows (SQLERRD(3) = 0) and we detect the conflict. This avoids holding database locks during the user's think time, which could be minutes.
30.11 MedClaim Scenario: Claim History Browse with Scrolling
Sarah Kim needs a CICS screen where customer service representatives can browse a member's complete claim history, scrolling through what could be hundreds of claims.
The Browse Architecture
- Representative enters Member ID and presses Enter
- Program queries all claims for that member and loads them into a TSQ
- Browse screen displays 15 claims per page
- PF7/PF8 scroll through the TSQ
- Typing 'S' next to a claim and pressing Enter shows the detail
- PF3 from detail returns to browse; PF3 from browse exits
Loading the Claims
2000-LOAD-MEMBER-CLAIMS.
*--------------------------------------------------------------
* Load all claims for a member into TSQ
*--------------------------------------------------------------
* Build unique TSQ name
STRING 'CLH' EIBTRMID
DELIMITED BY SIZE
INTO WS-CA-TSQ-NAME
END-STRING
* Clean up any existing queue
EXEC CICS
DELETEQ TS QUEUE(WS-CA-TSQ-NAME)
RESP(WS-RESP)
END-EXEC
EXEC SQL
DECLARE CLM-HIST-CURSOR CURSOR FOR
SELECT C.CLAIM_NUMBER, C.SERVICE_DATE,
C.CLAIM_AMOUNT, C.CLAIM_STATUS,
P.PROVIDER_NAME, C.SERVICE_CODE
FROM CLAIM C
JOIN PROVIDER P
ON C.PROVIDER_ID = P.PROVIDER_ID
WHERE C.MEMBER_ID = :WS-MEMBER-ID
ORDER BY C.SERVICE_DATE DESC
FETCH FIRST 500 ROWS ONLY
WITH UR
END-EXEC
EXEC SQL OPEN CLM-HIST-CURSOR END-EXEC
MOVE 0 TO WS-CA-TOTAL-ITEMS
PERFORM UNTIL SQLCODE = +100
OR WS-CA-TOTAL-ITEMS >= 500
EXEC SQL
FETCH CLM-HIST-CURSOR
INTO :WS-BRW-CLM-NUM,
:WS-BRW-SVC-DATE,
:WS-BRW-AMOUNT,
:WS-BRW-STATUS,
:WS-BRW-PROVIDER,
:WS-BRW-SVC-CODE
END-EXEC
IF SQLCODE = 0
ADD 1 TO WS-CA-TOTAL-ITEMS
EXEC CICS
WRITEQ TS QUEUE(WS-CA-TSQ-NAME)
FROM(WS-BRW-DATA)
LENGTH(WS-BRW-LEN)
ITEM(WS-TSQ-ITEM)
MAIN
RESP(WS-RESP)
END-EXEC
END-IF
END-PERFORM
EXEC SQL CLOSE CLM-HIST-CURSOR END-EXEC
IF WS-CA-TOTAL-ITEMS = 0
MOVE 'No claims found for this member'
TO MSGO
PERFORM 1500-SEND-SEARCH-DATAONLY
ELSE
MOVE 1 TO WS-CA-TOP-ITEM
MOVE 'B' TO WS-CA-STATE
PERFORM 3000-DISPLAY-BROWSE-PAGE
END-IF.
Date Range Filtering and Search Refinement
In production, Sarah Kim finds that many members have hundreds of claims spanning years. Loading all claims into the TSQ is wasteful when the representative only needs recent activity. The search screen includes optional date range fields:
2000-LOAD-MEMBER-CLAIMS.
*--------------------------------------------------------------
* Build dynamic WHERE clause based on search criteria
*--------------------------------------------------------------
* Check for date range filters
IF WS-FROM-DATE NOT = SPACES
AND WS-TO-DATE NOT = SPACES
EXEC SQL
DECLARE CLM-HIST-CURSOR CURSOR FOR
SELECT C.CLAIM_NUMBER, C.SERVICE_DATE,
C.CLAIM_AMOUNT, C.CLAIM_STATUS,
P.PROVIDER_NAME, C.SERVICE_CODE
FROM CLAIM C
JOIN PROVIDER P
ON C.PROVIDER_ID = P.PROVIDER_ID
WHERE C.MEMBER_ID = :WS-MEMBER-ID
AND C.SERVICE_DATE BETWEEN
:WS-FROM-DATE AND :WS-TO-DATE
ORDER BY C.SERVICE_DATE DESC
FETCH FIRST 500 ROWS ONLY
WITH UR
END-EXEC
ELSE
* No date filter — default to last 12 months
EXEC SQL
DECLARE CLM-HIST-ALL CURSOR FOR
SELECT C.CLAIM_NUMBER, C.SERVICE_DATE,
C.CLAIM_AMOUNT, C.CLAIM_STATUS,
P.PROVIDER_NAME, C.SERVICE_CODE
FROM CLAIM C
JOIN PROVIDER P
ON C.PROVIDER_ID = P.PROVIDER_ID
WHERE C.MEMBER_ID = :WS-MEMBER-ID
AND C.SERVICE_DATE >=
CURRENT DATE - 12 MONTHS
ORDER BY C.SERVICE_DATE DESC
FETCH FIRST 500 ROWS ONLY
WITH UR
END-EXEC
END-IF
The WITH UR (uncommitted read) isolation level is deliberate here. The browse screen is read-only, so dirty reads are acceptable, and UR avoids taking any locks — essential when browsing a table that is simultaneously being updated by batch adjudication programs.
📊 Performance Note: The date range filter dramatically reduces the number of rows loaded into the TSQ. Without it, a member with 10 years of history might have 2,000+ claims. With a default 12-month window, the typical result set drops to 20-50 claims — a 40x reduction in TSQ storage and DB2 processing time.
Asynchronous Adjudication Trigger
When a claim is submitted through the CICS screen, MedClaim does not adjudicate it immediately. Instead, the submission transaction uses START to trigger an asynchronous adjudication process:
7000-SUBMIT-CLAIM.
*--------------------------------------------------------------
* Insert the claim and trigger async adjudication
*--------------------------------------------------------------
EXEC SQL
INSERT INTO CLAIM
(CLAIM_NUMBER, MEMBER_ID, PROVIDER_ID,
SERVICE_CODE, SERVICE_DATE, CLAIM_AMOUNT,
CLAIM_STATUS, SUBMITTED_BY, SUBMIT_TS)
VALUES
(:WS-CLAIM-NUM, :WS-MEMBER-ID,
:WS-PROV-ID, :WS-SVC-CODE,
:WS-SVC-DATE, :WS-CLAIM-AMOUNT,
'SUBMITTED', :WS-USER-ID,
CURRENT TIMESTAMP)
END-EXEC
IF SQLCODE = 0
* Trigger async adjudication
MOVE WS-CLAIM-NUM TO WS-ADJ-CLAIM-NUM
MOVE WS-MEMBER-ID TO WS-ADJ-MEMBER-ID
EXEC CICS
START TRANSID('ADJD')
FROM(WS-ADJ-DATA)
LENGTH(WS-ADJ-LEN)
RESP(WS-RESP)
END-EXEC
IF WS-RESP = DFHRESP(NORMAL)
STRING 'Claim ' WS-CLAIM-NUM
' submitted. Adjudication pending.'
DELIMITED BY ' '
INTO MSGO
END-STRING
ELSE
* Claim is saved; adjudication will be
* picked up by the next batch run
STRING 'Claim ' WS-CLAIM-NUM
' submitted. Will be processed'
' in next batch cycle.'
DELIMITED BY ' '
INTO MSGO
END-STRING
END-IF
ELSE
STRING 'Submission failed: SQLCODE='
SQLCODE
DELIMITED BY SIZE
INTO MSGO
END-STRING
END-IF.
This pattern — immediate INSERT with asynchronous downstream processing — is a hallmark of well-designed CICS applications. The user gets an immediate confirmation that their data was saved, while the expensive adjudication logic runs in a separate task without tying up the user's terminal.
The Claim Submission Workflow
MedClaim also needs a multi-screen claim submission workflow:
Screen 1: Enter member ID → validate member exists
Screen 2: Enter claim details (provider, service, amount)
Screen 3: Review and confirm → INSERT into CLAIM table
The key challenge is validation at each step:
5000-VALIDATE-CLAIM-ENTRY.
*--------------------------------------------------------------
* Validate claim data before confirmation
*--------------------------------------------------------------
MOVE 'Y' TO WS-VALID-FLAG
* Validate provider exists and is in network
EXEC SQL
SELECT PROVIDER_NAME, NETWORK_STATUS
INTO :WS-PROV-NAME, :WS-NET-STATUS
FROM PROVIDER
WHERE PROVIDER_ID = :WS-PROV-ID
WITH UR
END-EXEC
IF SQLCODE = +100
MOVE 'Provider ID not found' TO MSGO
MOVE 'N' TO WS-VALID-FLAG
EXIT PARAGRAPH
END-IF
* Validate service code
EXEC CICS
LINK PROGRAM('SVCVALID')
COMMAREA(WS-SVC-VALIDATION)
LENGTH(WS-SVC-LEN)
RESP(WS-RESP)
END-EXEC
IF WS-SVC-RETURN-CODE NOT = 0
MOVE WS-SVC-RETURN-MSG TO MSGO
MOVE 'N' TO WS-VALID-FLAG
EXIT PARAGRAPH
END-IF
* Validate amount within limits
IF WS-CLAIM-AMOUNT > 99999.99
MOVE 'Amount exceeds maximum' TO MSGO
MOVE 'N' TO WS-VALID-FLAG
END-IF.
30.12 Defensive Programming in CICS
CICS error handling requires special vigilance because a misbehaving program affects all users sharing the CICS region.
The CICS Defensive Programming Checklist
| Practice | Why It Matters |
|---|---|
| Check RESP after every EXEC CICS command | Unchecked errors lead to unpredictable behavior |
| Handle MAPFAIL on every RECEIVE MAP | Users press Enter without typing — it is normal |
| Initialize output maps before populating | Prevents ghost data from previous interactions |
| Clean up TSQs on exit (PF3) and on first entry | Prevents storage leaks and stale data |
| Never use STOP RUN, COBOL file I/O, or ACCEPT | These crash or hang the CICS region |
| Keep transactions short (< 1 second) | Long tasks starve other users |
| Use unique TSQ names (include EIBTRMID) | Prevents cross-user data contamination |
| Implement HANDLE ABEND for recovery | Graceful degradation instead of cryptic errors |
| Use optimistic locking for updates | Avoids holding DB2 locks during think time |
| Log errors to TDQ before abending | Provides diagnostic information for support |
| Validate all user input before processing | Prevents garbage data and SQL errors |
| Test with multiple concurrent users | Single-user testing misses concurrency bugs |
Common Production Issues
Storage leaks from orphaned TSQs. If a user closes their terminal emulator instead of pressing PF3, the TSQ is never deleted. Implement a cleanup transaction that runs periodically (via START with INTERVAL) to delete TSQs older than a threshold.
*--------------------------------------------------------------
* TSQ cleanup transaction (runs every 30 minutes)
*--------------------------------------------------------------
PROCEDURE DIVISION.
* Browse all TSQs matching our prefix
EXEC CICS
STARTBROWSE TEMPQUEUE
RESP(WS-RESP)
END-EXEC
PERFORM UNTIL WS-RESP NOT = DFHRESP(NORMAL)
EXEC CICS
GETNEXT TEMPQUEUE(WS-TSQ-NAME)
RESP(WS-RESP)
END-EXEC
IF WS-RESP = DFHRESP(NORMAL)
IF WS-TSQ-NAME(1:3) = 'BRW'
OR WS-TSQ-NAME(1:3) = 'CLH'
* Check age and delete if stale
PERFORM 2000-CHECK-AND-DELETE
END-IF
END-IF
END-PERFORM
EXEC CICS
ENDBROWSE TEMPQUEUE
END-EXEC
* Schedule next cleanup
EXEC CICS
START TRANSID('CLNP')
INTERVAL(003000)
END-EXEC
EXEC CICS RETURN END-EXEC.
Cross-terminal data contamination. If two users run the same transaction with a hard-coded TSQ name like 'BRWQUEUE', their browse data will be interleaved in the same queue. Always include the terminal ID (EIBTRMID) in the TSQ name. At GlobalBank, the naming convention is PREFIX + EIBTRMID — for example, 'BRWT104' for a browse TSQ on terminal T104. Maria Chen enforces this in code reviews.
COMMAREA overwrite on first invocation. When a pseudo-conversational program is first invoked (EIBCALEN = 0), the DFHCOMMAREA pointer may be invalid. Referencing it causes an ASRA abend. The solution is the standard entry pattern:
0000-MAIN.
EVALUATE TRUE
WHEN EIBCALEN = 0
* First invocation — initialize
INITIALIZE WS-COMMAREA
PERFORM 1000-FIRST-TIME
WHEN OTHER
* Subsequent invocation — restore state
MOVE DFHCOMMAREA TO WS-COMMAREA
PERFORM 2000-PROCESS-INPUT
END-EVALUATE.
Transaction timeout. If a CICS task runs too long, CICS abends it with AKCT (task purge timeout). This typically indicates an infinite loop, a DB2 deadlock that was not handled, or a call to an external service that did not respond.
ASRA abends (program check). These are the CICS equivalent of a COBOL runtime error — division by zero, subscript out of range, invalid address. They are almost always caused by: 1. Referencing a field in the COMMAREA when EIBCALEN = 0 2. Array subscript exceeding the OCCURS count 3. Numeric field containing non-numeric data
🔴 The ASRA Trap: The most common cause of ASRA in pseudo-conversational programs is accessing DFHCOMMAREA when EIBCALEN is 0 (first invocation). Always check EIBCALEN before referencing DFHCOMMAREA. This is why the first line of your main logic should be
EVALUATE TRUE WHEN EIBCALEN = 0....
30.13 Putting It All Together: Transaction Design Patterns
Let us consolidate the patterns from this chapter and Chapter 29 into a reference of standard CICS transaction designs:
Pattern 1: Simple Inquiry (Chapter 29)
COMMAREA: State + Key
Screens: 1 (inquiry/display combined)
Flow: First → Send empty map → RETURN → Receive input → Lookup → Display → RETURN
Pattern 2: Browse with Detail (This Chapter)
COMMAREA: State + TSQ name + Position + Total + Search key
Storage: TSQ for browse data
Screens: 2 (search/browse combined, detail)
Flow: Search → Load TSQ → Browse → PF7/8 scroll → Select → Detail → PF3 → Browse
Pattern 3: Multi-Screen Maintenance (This Chapter)
COMMAREA: State + Key + Original values + New values
Screens: 3 (search, edit, confirm)
Flow: Search → Display → Edit → Validate → Confirm → UPDATE → Success message
Pattern 4: Wizard/Workflow (Advanced)
COMMAREA: State + Step number + Accumulated data
Storage: TSQ for large intermediate data
Screens: N (one per step)
Flow: Step 1 → Validate → Step 2 → ... → Step N → Confirm → Commit
PF3 at any step goes back one step (not exit)
Summary
This chapter has advanced your CICS skills from simple inquiry screens to production-quality multi-screen applications. You have learned to use Temporary Storage Queues for browse/scroll patterns and multi-screen state, Transient Data Queues for logging and event-driven processing, the START command for asynchronous operations, channels and containers for modern program communication, and HANDLE ABEND for error recovery.
The browse/scroll pattern — loading data into a TSQ, displaying pages, and handling PF7/PF8 — is one of the most common patterns in CICS programming. The multi-screen maintenance pattern — search, edit, confirm, commit with optimistic locking — is the other. Together, they account for the majority of CICS transactions in production today.
The defensive programming theme is particularly critical in CICS. Unlike batch programs where a bug affects one job, a bug in a CICS program can affect every user in the region. Check every RESP, handle every MAPFAIL, clean up every TSQ, and keep every transaction short.
In the next chapter, we move from the DB2/CICS world to explore IMS DB — an alternative database technology that predates DB2 and remains in wide use across many mainframe installations.
"I tell my team: if you write a CICS program that works perfectly for one user, you are halfway there. The other half is making it work perfectly when 2,000 users hit it simultaneously, when the DB2 subsystem is under load, and when someone turns off their terminal in the middle of a transaction. That is the real test." — James Okafor, team lead, MedClaim