This sample demonstrates how to integrate Apache Kafka with IBM CICS using Spring Boot, deployed as a WAR file to a CICS Liberty JVM server on z/OS. The sample includes both Gradle and Maven build configurations for use in Eclipse or standalone build environments.
The sample is intended both as a runnable example and as an educational reference for developers learning to build enterprise-grade Kafka consumers.
Key Features:
- Consumes messages from multiple Kafka topics asynchronously
- Processes each message within a CICS transaction context
- Demonstrates security identity propagation in Liberty
- Shows how to map different topics to different CICS transaction IDs
- Provides two alternative security approaches for credential management
- Overview
- Prerequisites
- Design and Architecture
- How It Works
- Security Models Explained
- Before You Start: Files to Modify
- Project Structure
- Thread Pool Management and TCLASS Considerations
- Downloading
- Building the Sample
- Deploying to a CICS Liberty JVM server
- Running the Sample
- Troubleshooting
- Logging Strategy
- License
- Additional Resources
- Contributing
This sample addresses a fundamental challenge: how to consume Kafka messages within CICS transactions while maintaining security context.
This sample has the following components:
- Uses Liberty's ManagedExecutorService - Ensures threads are CICS-aware
- Explicit Security Context Propagation - Captures and applies security identity.
- Per-Topic Transaction Mapping - Routes messages to appropriate CICS transactions based on topic
- Controlled Lifecycle Management - Allows dynamic start/stop of topic consumers via REST endpoints
Alternative A: Subject-Based RunAs Identity (Default)
┌─────────────────────────────────────────────────────────────────┐
│ Kafka Cluster │
│ (topics: orders, test-topic) │
└────────────────────────────┬────────────────────────────────────┘
│
│ Messages
▼
┌─────────────────────────────────────────────────────────────────┐
│ CICS Liberty JVM Server │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Spring Boot Application (WAR) │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ KafkaController (REST Endpoint) │ │ │
│ │ │ • /control/start?topic=xxx │ │ │
│ │ │ • /control/stop?topic=xxx │ │ │
│ │ │ • Captures caller's Subject (HTTP auth) │ │ │
│ │ └────────────┬─────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ KafkaConsumerService (Listeners) │ │ │
│ │ │ • @KafkaListener per topic │ │ │
│ │ │ • Sets RunAs Subject on consumer thread │ │ │
│ │ │ • Receives batches of messages │ │ │
│ │ └────────────┬─────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ KafkaMessageProcessor │ │ │
│ │ │ • Submits to ManagedExecutorService │ │ │
│ │ │ • Wraps in CICSTransactionRunnable │ │ │
│ │ └────────────┬─────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ Liberty ManagedExecutorService │ │ │
│ │ │ • CICS-aware thread pool │ │ │
│ │ │ • Inherits RunAs Subject │ │ │
│ │ └────────────┬─────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ CICSTransactionRunnable.run() │ │ │
│ │ │ • Executes under CICS transaction │ │ │
│ │ │ • Transaction ID from KafkaBatchConfig │ │ │
│ │ │ • Processes message business logic │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Alternative B: authData-Based Identity with Programmatic Login
The architecture is identical to Alternative A, with one key difference in the KafkaController:
┌──────────────────────────────────────────────────┐
│ KafkaController (REST Endpoint) │
│ • /control/start?topic=xxx │
│ • /control/stop?topic=xxx │
│ • Uses LoginManager.getSubject() │ ← Different from Alternative A
│ (programmatic login with authData) │
└──────────────────────────────────────────────────┘
│
▼
(rest of flow identical to Alternative A)
Key Difference:
- Alternative A: Subject from authenticated HTTP request (
WSSubject.getCallerSubject()) - Alternative B: Subject from programmatic login (
LoginManager.getSubject()using server.xml authData) - Both: Use the same RunAs mechanism (
WSSubject.setRunAsSubject(subject))
See Security Models Explained for detailed comparison.
| Component | Purpose | Key Methods/Annotations |
|---|---|---|
| KafkaApplication | Spring Boot entry point | @SpringBootApplication |
| KafkaController | REST API for life-cycle control | /start, /stop, captures Subject |
| KafkaConsumerService | Per-topic Kafka listeners | @KafkaListener, sets RunAs Subject |
| KafkaMessageProcessor | Async message processing | Submits to ManagedExecutorService |
| KafkaBatchConfig | Topic-to-transaction mapping | getTranIdForTopic() |
| LoginManager | Alternative: Subject via programmatic login | JAAS login using authData (optional) |
-
User Initiates Consumer
- HTTP GET/POST to
/control/start?topic=orders - KafkaController obtains a Subject (security identity):
- Alternative A (default): Captures caller's Subject from HTTP request
- Alternative B: Uses LoginManager to get Subject from authData
- Subject is stored in a map keyed by topic name
- Spring Kafka listener container for that topic is started
- HTTP GET/POST to
-
Kafka Messages Arrive
- Spring Kafka polls the broker and receives a batch of messages
- Messages are delivered to the appropriate
@KafkaListenermethod in KafkaConsumerService - Example:
onOrdersBatch()for the "orders" topic
-
Security Context is Applied
- The consumer thread calls
WSSubject.setRunAsSubject(subject) - This establishes the security identity for all subsequent work
- Liberty's ManagedExecutorService will inherit this identity
- The consumer thread calls
-
Message Processing is Offloaded
- Each message in the batch is wrapped in a
CICSTransactionRunnable - The runnable is submitted to Liberty's
ManagedExecutorService - This ensures the processing happens on a CICS-aware thread
- Each message in the batch is wrapped in a
-
CICS Transaction Executes
- The
CICSTransactionRunnable.run()method executes - The transaction ID is determined by
getTranid(), which looks up the topic in KafkaBatchConfig - Business logic processes the message (in this sample, just logging)
- The
-
User Stops Consumer
- HTTP GET/POST to
/control/stop?topic=orders - Spring Kafka listener container is stopped
- Subject mapping is removed from the map
- HTTP GET/POST to
This sample demonstrates per-topic listeners with individual life-cycle control:
@KafkaListener(id = "ordersListener", topics = "orders", ...)
public void onOrdersBatch(List<ConsumerRecord<String, String>> batch) { ... }
@KafkaListener(id = "test-topicListener", topics = "test-topic", ...)
public void onTestBatch(List<ConsumerRecord<String, String>> batch) { ... }Why separate listeners?
- Each topic can be started/stopped independently
- Different topics can use different CICS transaction IDs
- Allows per-topic security contexts (different users for different topics)
- Simplifies monitoring and troubleshooting
Transaction ID Mapping:
The application.properties file maps topics to transaction IDs:
cics.transaction.map.test-topic=KAFK
cics.transaction.map.orders=KAF1If no mapping exists, the default transaction ID CJSU is used.
This sample provides two alternative approaches for managing security credentials. Choose the one that best fits your operational requirements.
How it works:
- User authenticates to Liberty
/control/startcaptures the caller's Subject- Consumer thread sets this Subject as its RunAs identity
- ManagedExecutorService inherits the identity
- CICS transactions run under this identity
Configuration:
- No special server.xml configuration needed
- Security is established via the HTTP request
- Each topic can have a different identity (different users call
/start)
Pros:
- Simple configuration
- Flexible per-topic security
Code Location:
KafkaController.start()- Captures SubjectKafkaConsumerService.handleBatch()- Sets RunAs Subject
Alternative B: authData-Based Identity with Programmatic Login (Alternative - Not Active by Default)
How it works:
- Credentials are stored in Liberty's
server.xmlas<authData> - Password is AES-encrypted using a key from a RACF keyring
- Application performs programmatic JAAS login using these credentials via
LoginManager - Resulting Subject is obtained and used the same way as Alternative A
- This Subject is then set as RunAs identity on consumer threads (same mechanism as Alternative A)
Configuration Required:
- Create the RACF key ring and certificates
Run these TSO commands with appropriate IDs and DNs for your environment.
YOUR.KEYRING is the generic placeholder for your keyring name.
/* Create key ring (owned by Liberty STC user, e.g., <user_id>) */
RACDCERT ADDRING(YOUR.KEYRING) ID(<user_id>)
/* (Optional) Create a CERTAUTH root CA */
RACDCERT GENCERT CERTAUTH +
SUBJECTSDN(CN('MyLibertyCA') C('UK') O('YourOrg') OU('Liberty')) +
WITHLABEL('LIBERTY.CA') NOTAFTER(DATE(2030/12/31))
/* Create a personal certificate labeled 'Liberty' for <user_id> */
RACDCERT GENCERT ID(<user_id>) +
SUBJECTSDN(CN('liberty.example.com') C('UK') O('YourOrg') OU('Liberty')) +
WITHLABEL('Liberty') +
SIGNWITH(CERTAUTH LABEL('LIBERTY.CA')) +
RSA SIZE(2048) NOTAFTER(DATE(2028/12/31))
/* Connect CA + personal cert to the key ring */
RACDCERT ID(<user_id>) CONNECT(CERTAUTH LABEL('LIBERTY.CA') RING(YOUR.KEYRING))
RACDCERT ID(<user_id>) CONNECT(ID(<user_id>) LABEL('Liberty') RING(YOUR.KEYRING) USAGE(PERSONAL) DEFAULT)
/* Verify ring contents */
RACDCERT LISTRING(YOUR.KEYRING) ID(<user_id>)
Expected output:
Digital ring information for user <user_id>:
Ring:
>YOUR.KEYRING<
Certificate Label Name Cert Owner USAGE DEFAULT
-------------------------------- ------------ -------- -------
LIBERTY.CA CERTAUTH CERTAUTH NO
Liberty ID(<user_id>) PERSONAL YES
Reference: IBM Docs - JCERACFKS/JCECCARACFKS keyring setup
- Enable features in server.xml:
<feature>passwordUtilities-1.0</feature>
<feature>zosPasswordEncryptionKey-1.0</feature>- Configure RACF keyring in server.xml:
<!-- Obtain AES key from RACF key ring at runtime -->
<zosPasswordEncryptionKey
keyring="safkeyring:///YOUR.KEYRING"
type="JCERACFKS"
label="Liberty"/>
<!-- (Optional) Use RACF key ring as SSL keystore -->
<keyStore id="defaultKeyStore"
fileBased="false"
type="JCERACFKS"
location="safkeyring:///YOUR.KEYRING"
password="password"/>- Generate AES-encoded password with
securityUtility(key from RACF)
From ${wlp.install.dir}/wlp/bin, or from SSH shell on zFS:
securityUtility encode \
--encoding=aes \
--keyring=safkeyring:///YOUR.KEYRING \
--keyringType=JCERACFKS \
--keyLabel=Liberty \
"YourRacfPassword"This will output something like:
{aes}ARI673meZr9vyGHN8xKJdLx9...
- Define authData in server.xml:
<!-- Credentials alias (AES-protected) -->
<authData id="cicsSAF" user="<user_id>" password="{aes}ARI673meZr9vy...."/>- Activate LoginManager in code:
Uncomment in
KafkaController.java:
@Autowired(required = false)
private LoginManager loginManager;Then use in start() method:
Subject subject = loginManager.getSubject();Pros:
- Credentials managed centrally in server.xml
- No clear-text passwords in configuration
- Suitable for automated/service accounts
- Aligns with enterprise security policies
Cons:
- More complex setup (RACF keyring required)
- Requires z/OS security administrator involvement
Code Location:
LoginManager.java- Performs programmatic loginKafkaController.start()- Would use LoginManager instead of WSSubject.getCallerSubject()
- Java: Java 17 or later
- Build Tools:
- Gradle: Recommended: 8.0+ - included via wrapper
- Maven: Recommended: 3.9.0+ - included via wrapper
- IDE (Optional):
- Eclipse with IBM CICS SDK for Java EE, Jakarta EE and Liberty
- IntelliJ IDEA, VS Code, or any IDE with Gradle/Maven support
- Command line (no IDE required if using wrappers)
- CICS TS: V6.1 or later
- APAR PH70996: Must be applied to the CICS region
- WebSphere Liberty: Included with CICS
- Java: IBM Semeru Runtime 17 or later on z/OS
- Connectivity from z/OS to Kafka broker(s)
- HTTP/HTTPS access to Liberty server for REST API calls
Before building and deploying this sample, you must customize the following files with your environment-specific values:
File: cics-java-liberty-springboot-kafka-app/src/main/resources/application.properties
What to change:
# Replace with your Kafka broker address
spring.kafka.bootstrap-servers=<YOUR_KAFKA_BROKER_IP>:9092
# Optional: Change consumer group ID if needed
spring.kafka.consumer.group-id=<YOUR_CONSUMER_GROUP>
# Add topic-to-transaction mappings
cics.transaction.map.<your-topic>=<YOUR_TRANID>Example:
spring.kafka.bootstrap-servers=9.109.246.51:9092
spring.kafka.consumer.group-id=my-consumer-group
cics.transaction.map.test-topic=KAFK
cics.transaction.map.orders=KAF1File: etc/config/liberty/server.xml
For Alternative A (Subject-based - default):
- Verify that the following entry has been included in the configuration after CICS Initialization (auto-added if SEC=YES in SIT)
<feature>cicsts:security-1.0</feature>- Verify application location matches deployment path
For Alternative B (authData-based):
- See detailed setup instructions in Security Models Explained - Alternative B
- Requires: RACF keyring, AES-encrypted password, authData configuration
Files:
cics-java-liberty-springboot-kafka-cicsbundle/build.gradlecics-java-liberty-springboot-kafka-cicsbundle/pom.xml
When is this needed? Only if using Method 1: CICS Bundle Deployment (see Deploying to a CICS Liberty JVM server). This tells the CICS bundle plugins which Liberty JVM server will run your application.
What to change:
// Gradle: Set your target JVM server name
cics.jvmserver = '<YOUR_JVMSERVER_NAME>' // e.g., 'DFHWLP'<!-- Maven: Set your target JVM server name -->
<defaultjvmserver><YOUR_JVMSERVER_NAME></defaultjvmserver> <!-- e.g., DFHWLP -->Note: If using Method 2 (CICS Explorer) or Method 3 (Direct Liberty), you can skip this step.
Required changes:
- Uncomment
LoginManagerinKafkaController.java - Update
AUTH_DATA_IDinLoginManager.javato match your server.xml
See detailed instructions in: Security Models Explained - Alternative B, Step 6
Before building:
- Updated
application.propertieswith Kafka broker address - Configured topic-to-transaction mappings in
application.properties - Chose security Alternative (A or B)
- If Alternative A: Updated
server.xmlwith features - If Alternative B: Created RACF keyring and generated AES password
- If Alternative B: Updated
server.xmlwith features, keyring and authData - If Alternative B: Uncommented LoginManager in
KafkaController.java - Updated JVM server name in build files (if using CICS bundle deployment)
cics-java-liberty-springboot-kafka/
├── README.md # This file
├── LICENSE # EPL 2.0 license
├── build.gradle # Root Gradle build
├── settings.gradle # Gradle multi-project settings
├── pom.xml # Root Maven POM
├── gradlew / gradlew.bat # Gradle wrapper scripts
├── mvnw / mvnw.cmd # Maven wrapper scripts
│
├── cics-java-liberty-springboot-kafka-app/ # Main application
│ ├── build.gradle # App-level Gradle build
│ ├── pom.xml # App-level Maven POM
│ └── src/main/
│ ├── java/com/ibm/cicsdev/springboot/kafka
│ │ ├── KafkaApplication.java # Spring Boot entry point
│ │ ├── KafkaController.java # REST API for start/stop
│ │ ├── KafkaConsumerService.java # Kafka listeners per topic
│ │ ├── KafkaMessageProcessor.java # Async message processing
│ │ ├── KafkaBatchConfig.java # Topic-to-transaction mapping
│ │ ├── LoginManager.java # Optional: authData login
│ │ └── ServletInitializer.java # WAR deployment support
│ ├── resources/
│ │ └── application.properties # Kafka & Spring config
│ └── webapp/WEB-INF/
│ └── web.xml # Web app descriptor
│
├── cics-java-liberty-springboot-kafka-cicsbundle/ # CICS bundle (Gradle/Maven)
│ ├── build.gradle # Bundle Gradle build
│ ├── pom.xml # Bundle Maven POM
│ └── src/main/bundleParts/
│ └── KAFK.transaction # CICS transaction definition
│
├── cics-java-liberty-springboot-kafka-cicsbundle-eclipse/ # Eclipse CICS bundle project
│ ├── META-INF/cics.xml # Bundle manifest
│ └── cics-java-liberty-springboot-kafka.warbundle # WAR bundle part descriptor
│
└── etc/config/liberty/
└── server.xml # Liberty server template
In CICS Liberty environments with multiple applications sharing a single JVM server, proper thread pool management is critical to prevent application starvation and deadlocks. This section explains the challenges and solutions implemented in this sample.
Architecture Context:
- Multiple web applications run inside a single CICS Liberty JVM server
- Each application may have its own endpoint/virtualhost
- CICS regions are cloned for scalability/resilience
- Sysplex distributor load-balances between applications across cloned regions
The Challenge:
In traditional CICS, you use TCLASS to throttle applications and prevent any one application from consuming too much resource. However, in Liberty, this approach can cause deadlocks:
- HTTP requests enter Liberty and are allocated a thread from the shared pool
- Only then does the CICS intercepter check TCLASS limits
- If TCLASS limit is reached, the requesting thread is blocked
- The blocked thread is NOT released back to the thread pool
- Result: The entire server can deadlock, starving other applications
Why This Matters:
- Liberty in CICS has a maximum thread limit of 256
- Application starvation can occur at lower request rates than expected
- One misbehaving application can impact all applications in the JVM server
This sample implements custom ManagedExecutorService with application-specific thread limits to prevent starvation.
Instead of using Liberty's default executor (shared by all applications), each application defines its own executor with specific concurrency limits:
<!-- In server.xml -->
<managedExecutorService jndiName="concurrent/KafkaExecutor"
contextServiceRef="DefaultContextService"
maxThreads="10"
coreThreads="5"/>Benefits:
- ✅ Kafka application cannot consume more than 'maxThreads'
- ✅ Other applications in the same JVM server are protected
- ✅ Prevents deadlock scenarios with TCLASS
- ✅ Provides predictable resource allocation
The KafkaMessageProcessor injects the custom executor:
@Resource(lookup = "concurrent/KafkaExecutor")
private ManagedExecutorService executor;When Kafka messages arrive, they are submitted to this bounded executor:
executor.submit(new KafkaCICSTransactionRunnable(record, config));If the executor's queue is full, new submissions will block or be rejected (depending on policy), preventing unbounded thread consumption.
Consider these factors when setting maxThreads:
- Expected Message Rate: How many messages per second?
- Processing Time: How long does each message take to process?
- Other Applications: How many other apps share this JVM server?
- Total Thread Budget: CICS Liberty max is 256 threads
For Low-Volume Applications (< 100 msg/sec):
<managedExecutorService jndiName="concurrent/KafkaExecutor"
contextServiceRef="DefaultContextService"
maxThreads="10"
coreThreads="3"/>For Medium-Volume Applications (100-500 msg/sec):
<managedExecutorService jndiName="concurrent/KafkaExecutor"
contextServiceRef="DefaultContextService"
maxThreads="20"
coreThreads="5"/>For High-Volume Applications (> 500 msg/sec):
<managedExecutorService jndiName="concurrent/KafkaExecutor"
contextServiceRef="DefaultContextService"
maxThreads="40"
coreThreads="10"/>If using Eclipse: the simplest approach is to clone the repository using the Eclipse Git plugin (EGit) perspective.
If using the command line:
git clone https://github.com/cicsdev/cics-java-liberty-springboot-kafkaAlternatively, download the sample as a ZIP and unzip onto the workstation.
If importing into Eclipse:
- In the Git Repositories view, right-click the repository → Import as Project (imports the root project) (if you cloned from the command line, use File → Import → Existing Projects into Workspace instead, browse to the cloned directory, select all projects, and skip to step 6)
- Switch to the Java EE perspective
- In the Project Explorer, right-click the
cics-java-liberty-springboot-kafka-appfolder → Import as Project - Right-click the
cics-java-liberty-springboot-kafka-cicsbundlefolder → Import as Project - Right-click the
cics-java-liberty-springboot-kafka-cicsbundle-eclipsefolder → Import as Project - Required: Right-click the root project → Gradle → Refresh Gradle Project or Maven → Update Project... — this resolves Spring Boot and CICS dependencies into the project classpath. Without this step, the WTP export will produce an incomplete WAR missing
WEB-INF/lib.
Before building this sample, verify that the correct CICS TS bill of materials (BOM) is specified for your target release of CICS. The BOM specifies a consistent set of artifacts, and adds information about their scope. You can browse the published versions of the CICS BOM at Maven Central.
The sample is baselined on CICS TS V6.1. If you are running a later CICS release, update the BOM version to match — browse available versions at Maven Central.
Gradle (cics-java-liberty-springboot-kafka-app/build.gradle):
compileOnly(enforcedPlatform("com.ibm.cics:com.ibm.cics.ts.bom:6.1-20250812133513-PH63856"))
Maven (cics-java-liberty-springboot-kafka-app/pom.xml):
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.ibm.cics</groupId>
<artifactId>com.ibm.cics.ts.bom</artifactId>
<version>6.1-20250812133513-PH63856</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>You can build using Gradle, Maven, or Eclipse. The wrappers are pre-configured with compatible versions.
On Linux or Mac:
./gradlew clean buildOn Windows:
gradlew.bat clean buildThis creates a WAR file inside the cics-java-liberty-springboot-kafka-app/build/libs directory and a CICS bundle ZIP inside cics-java-liberty-springboot-kafka-cicsbundle/build/distributions.
Note: In Eclipse, the
builddirectory may be hidden by default. To view it: Package Explorer → ⋮ → Filters and Customization → uncheck "Gradle build folder". For Maven, thetargetdirectory is visible by default.
On Linux or Mac:
./mvnw clean verifyOn Windows:
mvnw.cmd clean verifyThis creates a WAR file inside the cics-java-liberty-springboot-kafka-app/target directory and a CICS bundle ZIP inside cics-java-liberty-springboot-kafka-cicsbundle/target.
-
Upload the bundle ZIP to zFS:
# From your workstation (using secure copy) scp cics-java-liberty-springboot-kafka-cicsbundle/build/distributions/*.zip user@zos:/path/to/bundles/
Note:
scpis a standard Unix/Linux command for secure file transfer. Replaceuser@zoswith your z/OS credentials and/path/to/bundles/with your target directory. -
Extract on z/OS:
# On z/OS cd /path/to/bundles jar xf cics-java-liberty-springboot-kafka-cicsbundle-1.0.0.zip
-
Define and install the bundle: See Common Bundle Installation Steps below.
This method uses IBM CICS Explorer (an Eclipse-based IDE) to create a CICS bundle and deploy it directly to z/OS. This approach is ideal for developers who prefer a GUI-based deployment workflow and want integrated tooling for CICS development.
Prerequisites:
- IBM CICS Explorer installed on your workstation
- CICS Explorer configured with connection to your z/OS system
- SSH/SFTP access to z/OS UNIX System Services (USS)
- CICS region configured and running
A CICS bundle is a deployment package that can contain multiple resources (WARs, JARs, OSGi bundles, etc.) and their metadata. The Bundle Project is already provided in the repository (imported when you cloned the repo).
-
Locate the Bundle Project: In Project Explorer, locate
cics-java-liberty-springboot-kafka-cicsbundle-eclipse(imported when you cloned the repository).Look into the project to verify its structure and contents.
-
Verify WAR Bundle Part Configuration:
- Locate the
cics-java-liberty-springboot-kafka.warbundlefile - Confirm that the JVM server is correctly specified (e.g.,
DFHWLP)
- Locate the
-
Review Bundle Files:
The bundle project contains:
cics-java-liberty-springboot-kafka-cicsbundle-eclipse/ ├── META-INF/ │ └── cics.xml # Bundle manifest └── cics-java-liberty-springboot-kafka.warbundle # WAR bundle part descriptorcics.xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <manifest xmlns="http://www.ibm.com/xmlns/prod/cics/bundle" id="cics-java-liberty-springboot-kafka-bundle" bundleMajorVer="1" bundleMinorVer="0" bundleMicroVer="0" bundleVersion="1" bundleRelease="0"> <define name="cics-java-liberty-springboot-kafka-app" type="http://www.ibm.com/xmlns/prod/cics/bundle/WARBUNDLE" path="cics-java-liberty-springboot-kafka.warbundle"/> </manifest>
id: Unique identifier for this bundle in CICS (cics-java-liberty-springboot-kafka-bundle)define: References the WAR bundle part
.warbundle file:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <warbundle symbolicname="cics-java-liberty-springboot-kafka-app" jvmserver="DFHWLP"/>
symbolicname: Must match the Eclipse project name of the-appproject (cics-java-liberty-springboot-kafka-app) so WTP can locate the WAR for exportjvmserver: Target Liberty JVM server name
This step deploys your bundle to z/OS and makes it available to CICS.
-
Initiate Export:
- In Project Explorer, right-click on your Bundle Project
- Select Export Bundle Project to z/OS UNIX File System
- Click Next
-
Specify Bundle Deployment Location:
Choose where on z/OS to deploy the bundle:
- Target directory:
/u/cicsts/bundles/cics-java-liberty-springboot-kafka-cicsbundle_1.0.0
Important Path Considerations:
- The directory will be created if it doesn't exist
Click Finish to start the export.
Ensure that the CICS region user has read and write access to the bundle directory structure.
- Target directory:
After export, define and install the bundle in your CICS region. See Common Bundle Installation Steps below.
-
Check Liberty Messages:
- View Liberty server logs
- Look for:
[AUDIT ] CWWKT0016I: Web application available (default_host): http://hostname:9080/cics-java-liberty-springboot-kafka/ -
Verify in CICS Explorer:
- Navigate to Bundle Definitions →
KFKABNDL - Status should show: Enabled and Installed
- Navigate to Bundle Definitions →
-
Upload WAR to zFS:
# From your workstation (using secure copy) scp cics-java-liberty-springboot-kafka-app/build/libs/*.war user@zos:/path/to/liberty/apps/
Note: Replace
user@zoswith your z/OS credentials and/path/to/liberty/apps/with your Liberty apps directory. -
Add to server.xml:
<application id="cics-java-liberty-springboot-kafka" location="${server.config.dir}/apps/cics-java-liberty-springboot-kafka.war" name="cics-java-liberty-springboot-kafka" type="war"> <application-bnd> <security-role name="cicsAllAuthenticated"> <special-subject type="ALL_AUTHENTICATED_USERS"/> </security-role> </application-bnd> </application>
-
Restart Liberty or refresh configuration
These steps apply to both Method 1 and Method 2 after the bundle is on z/OS.
Option A: Using CEDA Commands
-
Define the Bundle:
CEDA DEFINE BUNDLE(KFKABNDL) GROUP(MYGROUP) BUNDLEDIR(/u/cicsts/bundles/cics-java-liberty-springboot-kafka-cicsbundle-1.0.0) STATUS(ENABLED) -
Install the Bundle:
CEDA INSTALL BUNDLE(KFKABNDL) GROUP(MYGROUP) -
Enable (if needed):
CEDA SET BUNDLE(KFKABNDL) ENABLED
Option B: Using CICS Explorer UI
- Navigate to CICS SM (Systems Management) view
- Expand your CICS region → Bundle Definitions
- Right-click → New → Bundle Definition
- Fill in:
- Name:
KFKABNDL - Group:
MYGROUP - Bundle Directory:
/u/cicsts/bundles/cics-java-liberty-springboot-kafka-cicsbundle-1.0.0 - Status:
Enabled
- Name:
- Save and right-click → Install
Verify Installation:
- Check Liberty messages.log for application startup
- In CICS Explorer: Bundle Definitions →
KFKABNDLshould show Enabled and Installed
Check Liberty messages.log for successful application start:
[AUDIT ] CWWKT0016I: Web application available (default_host): http://hostname:9080/cics-java-liberty-springboot-kafka/
Alternative A (Subject-Based - Default):
Using curl with authentication:
curl -X POST "http://hostname:9080/cics-java-liberty-springboot-kafka/control/start?topic=test-topic" \
-u username:passwordUsing a browser (will prompt for credentials):
http://hostname:9080/cics-java-liberty-springboot-kafka/control/start?topic=test-topic
Alternative B (authData-Based):
Using curl (no authentication required - uses programmatic login):
curl -X POST "http://hostname:9080/cics-java-liberty-springboot-kafka/control/start?topic=test-topic"Expected Response (both alternatives):
Started listener for topic=test-topic
Check Liberty messages.log:
[INFO] Received message from topic test-topic: Hello from Kafka!
[INFO] Task USERID = TESTUSER
[INFO] DEBUG: Topic = test-topic
[INFO] DEBUG: Finished processing Kafka message in thread: Default Executor-thread-1 Hello from Kafka!
Alternative A (Subject-Based - Default):
curl -X POST "http://hostname:9080/cics-java-liberty-springboot-kafka/control/stop?topic=test-topic" \
-u username:passwordAlternative B (authData-Based):
curl -X POST "http://hostname:9080/cics-java-liberty-springboot-kafka/control/stop?topic=test-topic"Expected Response (both alternatives):
Stopped listener for topic=test-topic
You can run multiple consumers simultaneously. Each topic runs independently with its own transaction ID:
Start consumer for orders topic:
curl -X POST ".../control/start?topic=orders" -u username:passStart consumer for test-topic:
curl -X POST ".../control/start?topic=test-topic" -u username:passSymptom: WSSecurityException in logs
Cause: Subject is null or invalid
Solution:
- Verify user is authenticated when calling
/start - Check Liberty security configuration
- For Alternative B: Verify authData is configured correctly
Symptom: "Failed to decrypt password" or login failure
Cause: RACF keyring or AES key misconfigured
Solution:
- Verify keyring exists:
RACDCERT LISTRING(<keyring>) ID(<userid>) - Verify certificate label matches:
label="Liberty" - Regenerate AES password with correct keyring path
- Ensure Liberty user has access to keyring
Enable detailed logging in server.xml:
<logging traceSpecification="*=info:com.ibm.cicsdev.springboot.kafka.*=all"/>Key log locations:
- Liberty:
${wlp.user.dir}/servers/<server>/logs/messages.log - CICS: MSGUSR, CSSL transient data queues
- Kafka: Check broker logs if messages aren't being produced
This sample uses Java Util Logging (JUL) for simplicity and integration with Liberty.
Why JUL?
- Built into JDK (no dependencies)
- Integrates with Liberty's logging infrastructure
- Thread-safe and efficient
Why not System.out.println?
- Not thread-safe
- Poor performance under concurrency
- Bypasses Liberty logging configuration
Why not Log4j/SLF4J?
- Adds unnecessary dependencies for a sample
- Introduces classloader complexity
- JUL is sufficient for this use case
Viewing logs:
By default, JUL output appears in messages.log. Ensure this JVM option is set:
-Dcom.ibm.ws.logging.console.log.level=INFO
This project is licensed under Eclipse Public License - v 2.0.
By downloading, installing, and/or using this sample, you acknowledge that separate license terms may apply to any dependencies required for installation, execution, or automated builds, including IBM CICS development components: https://www.ibm.com/support/customer/csol/terms/?id=L-ACRR-BBZLGX
This sample is maintained by IBM CICS development. We welcome bug reports and feature requests via GitHub Issues. Contributions are welcome and reviewed on a case-by-case basis — please read the contributing guidelines before opening a pull request. For CICS product questions, contact IBM Support.