Part 0 — Getting Started¶
0.1 Quick Start Overview¶
This guide will help you compile and run your first PL/I program on the Heirloom PLI Platform in under 30 minutes.
What You'll Need¶
- Java 11 or later (JDK) —
java -version - Gradle (or Maven) — Build system
- PL/I Compiler (
pli_compiler2) — Version 26.2.27.RC1 or later - PL/I Runtime (
pli_runtime2) — Version 26.2.27.RC1 or later - Manifold (
heirloom-manifold) — Version 2.0.0 or later
Installation Check¶
# Check Java version
java -version # Should show Java 11+
# Check Gradle
gradle --version # Should show Gradle 7.0+
# Verify PLI Compiler JAR
ls -lh pli_compiler2-*.jar
# Verify PLI Runtime JAR
ls -lh pli_runtime2-*.jar
0.2 Hello World Example¶
Let's create a simple PL/I program and compile it to Java.
Step 1: Create PL/I Source¶
Create a file named HELLO.pli:
HELLO: PROCEDURE OPTIONS(MAIN);
DCL MESSAGE CHAR(30);
MESSAGE = 'Hello from PL/I!';
PUT SKIP LIST(MESSAGE);
END HELLO;
Step 2: Compile to Java¶
Run the PLI Compiler:
java -jar pli_compiler2-26.2.27.RC1.jar \
--source HELLO.pli \
--target ./generated \
--package com.example \
--strategy static
Output:
Step 3: Examine Generated Java¶
Look at the generated Java code:
Generated Java (actual):
package com.example;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Optional;
import com.heirloomcomputing.epli.runtime.*;
import com.heirloomcomputing.sql.*;
import com.heirloomcomputing.mq.*;
import com.heirloomcomputing.sql.types.*;
import com.heirloomcomputing.cics.*;
import static com.heirloomcomputing.cics.CICSResponseOption.*;
public class HELLO extends ETPBatch {
@CHAR(30)
private static PLIString message = new PLIString(30);
public static void main(String[] args) {
message.set("Hello from PL/I!");
System.out.printf("%s%s", message.toString(), "\n");
}
}
Key Differences from Standard Java: - PLIString instead of String — Provides EBCDIC support and PL/I semantics - Initialized with new PLIString(length) and set with .set() - PUT SKIP LIST becomes System.out.printf() — Standard Java I/O - Static fields for batch programs (--strategy static)
See also: - Runtime Reference: 4.2.3 String Types for PLIString API details - Runtime Reference: 4.1.1 Base Classes for ETPBatch vs ETPBase - Manifold Enhancement: 4.5.2 Annotation Reference for @CHAR annotation
Step 4: Compile Java with Manifold¶
Create build.gradle:
plugins {
id 'java'
}
repositories {
mavenCentral()
maven { url 'https://your-heirloom-repo' }
}
dependencies {
implementation 'com.heirloom:pli_runtime2:26.2.27.RC1'
annotationProcessor 'com.heirloom:heirloom-manifold:2.0.0'
implementation 'systems.manifold:manifold-props-rt:2024.1.42'
}
sourceSets {
main {
java {
srcDirs = ['generated']
}
}
}
tasks.withType(JavaCompile) {
options.compilerArgs += [
'-Xplugin:Manifold',
'--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED',
'--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED',
'--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED',
'--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED'
]
}
task run(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
mainClass = 'com.example.HELLO'
}
Compile:
Step 5: Run the Program¶
Output:
Congratulations! You've successfully compiled and run your first PL/I program on the Heirloom PLI Platform.
0.3 Structure Example (Data Declarations)¶
Let's explore PL/I structures (records) and see how they translate to Java.
PL/I Structure¶
Create CUSTOMER.pli:
CUSTOMER: PROCEDURE OPTIONS(MAIN);
DCL 1 CUSTOMER_REC,
2 CUST_ID CHAR(10),
2 NAME CHAR(30),
2 ADDRESS,
3 STREET CHAR(30),
3 CITY CHAR(20),
3 ZIP CHAR(10),
2 BALANCE DEC FIXED(15,2),
2 STATUS CHAR(1);
/* Initialize customer data */
CUST_ID = 'C0001';
NAME = 'John Smith';
STREET = '123 Main St';
CITY = 'New York';
ZIP = '10001';
BALANCE = 1234.56;
STATUS = 'A';
/* Display customer info */
PUT SKIP LIST('Customer ID:', CUST_ID);
PUT SKIP LIST('Name:', NAME);
PUT SKIP LIST('City:', CITY);
PUT SKIP LIST('Balance:', BALANCE);
END CUSTOMER;
Compile and Run¶
# Compile PL/I to Java
java -jar pli_compiler2-26.2.27.RC1.jar \
--source CUSTOMER.pli \
--target ./generated \
--package com.example \
--strategy static
# Build and run
gradle build
gradle run -PmainClass=com.example.CUSTOMER
Output:
Understanding the Generated Code¶
The PL/I structure is transpiled to:
- Nested Java Classes (actual generated code):
public class CUSTOMER extends ETPBatch { public static Customer_rec customer_rec = new Customer_rec(); public static class Customer_rec extends Group { @CHAR(10) public PLIString cust_id = new PLIString(this, 10); @CHAR(30) public PLIString name = new PLIString(this, 30); public Address address = new Address(); @DECIMAL(15, 2) public BigDecimal balance; @CHAR(1) public PLIString status = new PLIString(this, 1); public Customer_rec() { this._anon = new ByteBuilder(this.length()); this._anon.setBuffer(this.toBytes()); } public static class Address extends Group { @CHAR(30) public PLIString street = new PLIString(this, 30); @CHAR(20) public PLIString city = new PLIString(this, 20); @CHAR(10) public PLIString zip = new PLIString(this, 10); public Address() { this._anon = new ByteBuilder(this.length()); this._anon.setBuffer(this.toBytes()); } } } }
Note: All PL/I CHAR types become PLIString with proper initialization: - new PLIString(this, length) — Associates field with parent Group - Constructor initializes ByteBuilder for memory layout - EBCDIC encoding support for mainframe compatibility - Fixed-length and varying-length string semantics
- Manifold Enhancement:
- Calculates byte offsets: CUST_ID=0, NAME=10, ADDRESS=40, etc.
- Generates
$MetadataOffsetstable -
Creates getter/setter methods
-
Runtime Execution:
Groupclass handles memory layout- Fields can be serialized to byte arrays (for EBCDIC interchange)
See also: - Runtime Reference: 4.2.4 Structures for Group and structure field handling - Runtime Reference: 4.3 Storage Management for ByteBuilder and memory layout - Manifold Enhancement: 4.5.3 Compilation Process for offset calculation details - Language Reference: 3.3 Structures for PL/I structure syntax
0.4 Simple CICS Program Example¶
Let's create a minimal CICS transaction handler.
PL/I CICS Program¶
Create INQUIRY.pli:
INQUIRY: PROCEDURE;
DCL CUSTOMER_ID CHAR(10);
DCL CUSTOMER_NAME CHAR(30);
DCL CUSTOMER_BALANCE DEC FIXED(15,2);
/* Receive input from terminal */
EXEC CICS RECEIVE
INTO(CUSTOMER_ID)
LENGTH(10);
/* Retrieve customer from database */
EXEC SQL
SELECT NAME, BALANCE
INTO :CUSTOMER_NAME, :CUSTOMER_BALANCE
FROM CUSTOMERS
WHERE CUSTOMER_ID = :CUSTOMER_ID;
/* Send response to terminal */
EXEC CICS SEND
FROM(CUSTOMER_NAME)
LENGTH(30);
EXEC CICS RETURN;
END INQUIRY;
Compile with CICS and SQL¶
java -jar pli_compiler2-26.2.27.RC1.jar \
--source INQUIRY.pli \
--target ./generated \
--package com.example.cics \
--strategy instance \
--cics \
--sql
Note: --strategy instance is used for CICS programs (thread-safe, instance variables).
Generated Java (actual)¶
package com.example.cics;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Optional;
import com.heirloomcomputing.epli.runtime.*;
import com.heirloomcomputing.sql.*;
import com.heirloomcomputing.mq.*;
import com.heirloomcomputing.sql.types.*;
import com.heirloomcomputing.cics.*;
import static com.heirloomcomputing.cics.CICSResponseOption.*;
public class INQUIRY extends ETPBase {
@CHAR(10)
private PLIString customer_id = new PLIString(this, 10);
@CHAR(30)
private PLIString customer_name = new PLIString(this, 30);
@DECIMAL(15, 2)
private BigDecimal customer_balance;
private static SQLCA sqlca = SQLBuilder.getInstance().getSqlca();
private CICSResult cics;
@Override
public void cicsInvoke(Pointer<?> commAreaPtr) {
// EXEC CICS RECEIVE
cics = CICSBuilder.getInstance(this)
.receive(_transenv)
.into(customer_id)
.length(10)
.execute(this);
// EXEC SQL SELECT
sqlca = new SQLSelect()
.fields("NAME", "BALANCE")
.into(customer_name, customer_balance)
.from("CUSTOMERS")
.where("CUSTOMER_ID = ?", customer_id)
.execute(this);
// EXEC CICS SEND
cics = CICSBuilder.getInstance(this)
.send(_transenv)
.from(customer_name)
.length(30)
.execute(this);
// EXEC CICS RETURN
cics = CICSBuilder.getInstance(this)
.cicsReturn(_transenv)
.execute(this);
}
}
Key Points: - All CHAR types are PLIString initialized with new PLIString(this, length) - CICS commands use CICSBuilder.getInstance(this) singleton pattern - Fluent API: .commandName(_transenv).option1().option2().execute(this) - Transaction environment (_transenv) passed to command methods (not execute) - SQL operations return SQLCA (SQL Communication Area) - CICS operations return CICSResult with response codes
See also: - CICS Support: 5.2 Command Reference for complete CICS API documentation - SQL Support: 6.2 SQL Operations for SQLCA and database operations - Runtime Reference: 4.4.1 QR TCB Lock Management for thread safety in CICS transactions - Runtime Reference: 4.1.1 Base Classes for ETPBase details - Real-World Examples: 12.1 CICS Application for enterprise application structure
Deployment Configuration¶
CICS programs require transaction configuration in deploy.properties located at src/main/webapp/WEB-INF/classes/deploy.properties:
# Program Declarations - Map program names to Java classes
program.inquiry=com.example.cics.INQUIRY
# Transaction Declarations - Map transaction IDs to programs
transaction.inq1=inquiry
transaction.inq1.uctran=false
# SQL Configuration
sql.default.autocommit=false
sql.default.isolation=read committed
sql.default.datasource=jdbc/mydb
# System Configuration
sysid=MYSYS
applid=MYAPPL
Key Properties:
| Property | Purpose | Example |
|---|---|---|
program.<name> | Map program name to Java class | program.inquiry=com.example.cics.INQUIRY |
transaction.<transid> | Map transaction ID to program | transaction.inq1=inquiry |
transaction.<transid>.uctran | Uppercase translation on/off | transaction.inq1.uctran=false |
sql.default.datasource | JNDI datasource for SQL | sql.default.datasource=jdbc/mydb |
sysid | System identifier (4 chars) | sysid=MYSYS |
applid | Application identifier | applid=MYAPPL |
See also: Build Integration: 13.3 Deployment Configuration for complete deploy.properties reference
Build and Deploy¶
# Build WAR file
gradle war
# Deploy to Payara
asadmin deploy build/libs/inquiry.war
# Access transaction via HTTP
curl http://localhost:8080/cics/servlet?transid=INQ1&username=USER01
# Or via TN3270 terminal emulator
# Connect to: localhost:8023
# Enter: INQ1
WAR Structure:
inquiry.war
├── WEB-INF/
│ ├── classes/
│ │ ├── com/example/cics/INQUIRY.class
│ │ ├── deploy.properties ← Transaction configuration
│ │ └── simplelogger.properties
│ ├── lib/
│ │ ├── pli_runtime2-*.jar
│ │ └── [other dependencies]
│ └── web.xml
└── [BMS maps and resources]
0.5 Simple Batch Job Example¶
Let's create a batch program that processes a file.
PL/I Batch Program¶
Create FILEPROC.pli:
FILEPROC: PROCEDURE OPTIONS(MAIN);
DCL INPUT_FILE FILE RECORD INPUT;
DCL OUTPUT_FILE FILE RECORD OUTPUT;
DCL 1 INPUT_REC,
2 RECORD_ID CHAR(10),
2 AMOUNT DEC FIXED(15,2);
DCL TOTAL_AMOUNT DEC FIXED(15,2);
DCL RECORD_COUNT FIXED BIN(31);
TOTAL_AMOUNT = 0;
RECORD_COUNT = 0;
/* Open files */
OPEN FILE(INPUT_FILE);
OPEN FILE(OUTPUT_FILE);
/* Process records */
ON ENDFILE(INPUT_FILE) GO TO DONE;
DO WHILE('1'B);
READ FILE(INPUT_FILE) INTO(INPUT_REC);
TOTAL_AMOUNT = TOTAL_AMOUNT + AMOUNT;
RECORD_COUNT = RECORD_COUNT + 1;
WRITE FILE(OUTPUT_FILE) FROM(INPUT_REC);
END;
DONE:
/* Close files */
CLOSE FILE(INPUT_FILE);
CLOSE FILE(OUTPUT_FILE);
/* Print summary */
PUT SKIP LIST('Records processed:', RECORD_COUNT);
PUT SKIP LIST('Total amount:', TOTAL_AMOUNT);
END FILEPROC;
Compile for Batch¶
java -jar pli_compiler2-26.2.27.RC1.jar \
--source FILEPROC.pli \
--target ./generated \
--package com.example.batch \
--strategy static
Create JCL Job¶
Create FILEPROC.jcl:
//FILEPROC JOB (ACCT),'File Processing Job',CLASS=A
//STEP01 EXEC PGM=com.example.batch.FILEPROC
//INPUT DD DSN=INPUT.DATA.FILE,DISP=SHR
//OUTPUT DD DSN=OUTPUT.DATA.FILE,
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(5,1))
//SYSOUT DD SYSOUT=*
Submit to EBP¶
How EBP Executes Batch Programs:
Unlike CICS applications (which require deploy.properties configuration), batch programs are executed by calling the main() method directly:
JCL: EXEC PGM=com.example.batch.FILEPROC
↓
EBP invokes: com.example.batch.FILEPROC.main(args)
↓
Batch program executes with static fields
No deployment configuration needed — EBP reads the fully-qualified class name from the JCL EXEC PGM= statement and invokes public static void main(String[] args).
Submit Job to EBP:
# Via REST API
curl -X POST http://localhost:8080/ebp/api/jobs \
-H "Content-Type: text/plain" \
-d @FILEPROC.jcl
# Via Web UI
# Navigate to http://localhost:8080/ebp
# Click "Submit Job", upload FILEPROC.jcl
Key Differences: CICS vs Batch:
| Aspect | CICS (Online) | Batch (EBP) |
|---|---|---|
| Execution | Via transaction ID in deploy.properties | Direct main() method call |
| Configuration | Requires deploy.properties | No configuration needed |
| Base Class | Extends ETPBase | Extends ETPBatch |
| Strategy | --strategy instance | --strategy static |
| Variables | Instance fields (thread-safe) | Static fields (single-threaded) |
| Entry Point | cicsInvoke(Pointer commAreaPtr) | main(String[] args) |
See also: - EBP: 11.2 JCL Syntax for JCL command reference - EBP: 11.3 Job Submission for submitting batch jobs - File I/O: 8.2 Sequential Files for file processing patterns - Real-World Examples: 12.2 Batch Application for multi-wave batch architecture
0.6 Build Automation¶
Complete Gradle Project Structure¶
my-pli-project/
├── build.gradle
├── settings.gradle
├── src/
│ ├── main/
│ │ ├── pli/ ← PL/I source files
│ │ │ ├── HELLO.pli
│ │ │ ├── CUSTOMER.pli
│ │ │ └── INQUIRY.pli
│ │ ├── jcl/ ← JCL job definitions (for batch)
│ │ │ └── FILEPROC.jcl
│ │ └── resources/
│ └── test/
│ └── java/
└── generated/ ← Generated Java source (created by compiler)
Complete build.gradle¶
plugins {
id 'java'
id 'war' // For CICS applications
}
group = 'com.example'
version = '1.0.0'
repositories {
mavenCentral()
maven { url 'https://your-heirloom-repo' }
}
dependencies {
// PLI Runtime
implementation 'com.heirloom:pli_runtime2:26.2.27.RC1'
// Manifold (compile-time processor)
annotationProcessor 'com.heirloom:heirloom-manifold:2.0.0'
implementation 'systems.manifold:manifold-props-rt:2024.1.42'
// For CICS applications
implementation 'jakarta.platform:jakarta.jakartaee-api:10.0.0'
// For SQL/JDBC
implementation 'com.ibm.db2:jcc:11.5.8.0'
// Testing
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
}
// Configure PLI compilation task
task compilePli(type: Exec) {
workingDir projectDir
def pliSource = file('src/main/pli')
def javaTarget = file('generated')
commandLine 'java', '-jar', 'pli_compiler2-26.2.27.RC1.jar',
'--source', pliSource,
'--target', javaTarget,
'--package', 'com.example',
'--strategy', project.findProperty('strategy') ?: 'static',
'--cics',
'--sql'
inputs.dir(pliSource)
outputs.dir(javaTarget)
}
// Run PLI compilation before Java compilation
compileJava.dependsOn compilePli
sourceSets {
main {
java {
srcDirs = ['generated', 'src/main/java']
}
}
}
// Manifold javac configuration
tasks.withType(JavaCompile) {
options.compilerArgs += [
'-Xplugin:Manifold',
'--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED',
'--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED',
'--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED',
'--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED',
'--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED',
'--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED'
]
options.fork = true
}
// Run a specific program
task runProgram(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
mainClass = project.findProperty('mainClass') ?: 'com.example.HELLO'
args = project.findProperty('args')?.split(',') ?: []
}
// Create WAR for CICS deployment
war {
archiveBaseName = 'my-cics-app'
}
Build Commands¶
# Compile all PL/I programs to Java
gradle compilePli
# Compile Java (with Manifold enhancement)
gradle compileJava
# Or do both with:
gradle build
# Run a specific program
gradle runProgram -PmainClass=com.example.CUSTOMER
# Build WAR for CICS deployment
gradle war
# Clean and rebuild
gradle clean build
0.7 Next Steps¶
Now that you've completed the getting started guide, explore these topics:
Learning Path¶
- Compiler Reference (Part 2)
- Learn all compiler options
- Understand preprocessor directives
-
Explore SQL and CICS integration
- Study PLI data types in detail
- Learn storage management (BASED, DEFINED)
-
Explore built-in functions
- Understand compile-time processing
- Learn annotation reference
-
Master memory layout calculations
- Study enterprise CICS application structure
- Learn batch processing patterns
-
Understand program organization (H1/H8/H9)
- Advanced Gradle configuration
- CI/CD pipeline setup
- Deployment strategies
Common Next Steps¶
For CICS Developers: - Read Part 5: CICS Support - Study transaction routing and BMS maps - Explore Helper system for customization
For Batch Developers: - Read Part 11: EBP - Elastic Batch Platform - Learn JCL syntax and job submission - Study multi-wave batch architecture
For Architects: - Review Part 1: Architecture Overview - Study Part 10: Migration Guide - Read Part 15: Best Practices