Skip to content

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:

Compiling HELLO.pli...
Generated: ./generated/com/example/HELLO.java
Compilation successful.

Step 3: Examine Generated Java

Look at the generated Java code:

cat ./generated/com/example/HELLO.java

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:

gradle build

Step 5: Run the Program

gradle run

Output:

Hello from PL/I!

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:

Customer ID: C0001
Name: John Smith
City: New York
Balance: 1234.56

Understanding the Generated Code

The PL/I structure is transpiled to:

  1. 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

  1. Manifold Enhancement:
  2. Calculates byte offsets: CUST_ID=0, NAME=10, ADDRESS=40, etc.
  3. Generates $MetadataOffsets table
  4. Creates getter/setter methods

  5. Runtime Execution:

  6. Group class handles memory layout
  7. 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

  1. Compiler Reference (Part 2)
  2. Learn all compiler options
  3. Understand preprocessor directives
  4. Explore SQL and CICS integration

  5. Runtime Reference (Part 4)

  6. Study PLI data types in detail
  7. Learn storage management (BASED, DEFINED)
  8. Explore built-in functions

  9. Manifold Enhancement (Part 4.5)

  10. Understand compile-time processing
  11. Learn annotation reference
  12. Master memory layout calculations

  13. Real-World Examples (Part 12)

  14. Study enterprise CICS application structure
  15. Learn batch processing patterns
  16. Understand program organization (H1/H8/H9)

  17. Build Integration (Part 13)

  18. Advanced Gradle configuration
  19. CI/CD pipeline setup
  20. 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