IBM Support

Java interoperability with PL/I

General Page

This technote gives a brief description of Java and the Java Native Interface (JNI) and explains why you might be interested in using it with PL/I. This technote focuses on 64-bit mode.

A simple Java-PL/I application is described, and the technote provides information about compatibility between the two languages. Instructions on how to build and run the Java-PL/I sample application assume that the work is being done in the z/OS® UNIX System Services environment of z/OS.
Before you can communicate with Java from PL/I, you need to have Java installed on your z/OS system. Contact your local System Administrator for more information about how to set up your z/OS Java environment. For more information, refer to the IBM Java’s support website.

The sample program has been compiled and tested with IBM SDK for z/OS, Java Technology Edition Version 8.0, and IBM Semeru Runtime Certified Edition for z/OS Version 11.0. The example below uses IBM Semeru Version 11.0. To determine the level of Java in your z/OS UNIX System Services environment, enter this command from the command line:
java -version

The active Java version is then displayed, as in the following example:
java version "11.0.15" 2022-04-19
IBM Semeru Runtime Certified Edition for z/OS 11.0.15.0 (build 11.0.15+10)
IBM J9 VM 11.0.15.0 (build z/OS-Release-11.0.15.0-b01, JRE 11 z/OS s390x-64-Bit
IBM Java Version 8.0 is available in 31-bit and 64-bit. IBM Semeru Version 11.0 is 64-bit only.
For communication between 64-bit Java and 31-bit PL/I, we recommend having the 64-bit Java program call a 64-bit PL/I interface routine, which can call the 31-bit PL/I routine.  For PL/I 31-bit and 64-bit interoperability, the Enterprise PL/I for z/OS 6.1 compiler is required. For more details about this feature, please refer to Mixing amode 31 and amode 64 code in the Enterprise PL/I for z/OS 6.1 Programming Guide.   

Java Native Interface (JNI)
Java provides a platform for building highly robust, scalable and reliable modern enterprise apps.
Developers can build batch and transactional apps, microservices, and more by using Java’s APIs, libraries, and frameworks. The Java Native Interface (JNI) is the Java interface to native programming languages and is part of the Java Development Kits.
Writing programs that use JNI is one way of ensuring that your code is portable across many platforms.

The JNI allows Java code that runs within a Java virtual machine (JVM) to operate with applications and libraries written in other languages, such as PL/I. In addition, the Invocation API allows you to embed a Java virtual machine into your PL/I applications.
Java is a fairly complete programming language; however, there are situations in which you want to call a program written in another programming language. You can do this from Java with a method call to a native language, known as a native method.
These are some reasons to use native methods:
  • The native language has a special capability that your application needs and that the standard Java class libraries lack.
  • You already have many existing applications in your native language and you want to make them accessible to a Java application.
  • You want to implement an intensive series of complicated calculations in your native language and have your Java applications call these functions.
  • You or your programmers have a broader skill set in your native language and you do not want to loose this advantage.
Programming through the JNI lets you use native methods to do many different operations:
  • A native method can use Java objects in the same way that a Java method uses these objects.
  • A native method can create Java objects, including arrays and strings, and then inspect and use these objects to perform its tasks.
  • A native method can inspect and use objects created by Java application code.
  • A native method can update Java objects that it created or were passed to it, and these updated objects can then be made available to the Java application.

Finally, native methods can also easily call the existing Java methods, capitalizing on the functionality already incorporated in the Java programming framework. In these ways, both the native language side and the Java side of an application can create, update, and access Java objects and then share these objects between them.
With Java, the use of direct byte buffers allows it to access data stored in memory owned by the native application. This off-heap (off Java heap) memory is one way of marshalling data across language boundaries, which helps with performance. Refer to the IBM Java manuals for more detailed information.  
Calling PL/I program from Java

When a PL/I program is called from a Java program, all the files opened in PL/I must be explicitly closed in PL/I before PL/I returns control to Java for the last time.

Similarly, all the modules that are fetched in the PL/I program must be released.

JNI sample program - 'Hello World'
This sample program is another variation of the "Hello World!" program. The "Hello World!" program has one Java class, callingPLI.java. The native method, written in PL/I, is contained in hiFromPLI.pli.

Here is a brief overview of the steps to create this sample program:
Step 1. Write a Java program that defines a class containing a native method, loads the native load library, and calls the native method.
Step 2. Compile the Java program to create a Java class.
Step 3. Write a PL/I program that implements the native method and displays the "Hello!" text.
Step 4. Compile and link the PL/I program.
Step 5. Run the Java program that calls the native method in the PL/I program.
Step 1: Writing a Java program
1. Declare the native method.
All the methods, whether Java methods or native methods, must be declared within a Java class. The only difference in the declaration of a Java method and a native method is the keyword native. The native keyword tells Java that the implementation of this method will be found in a native library that will be loaded during the execution of the program. You can declare the native method as follows:
public native void callToPLI();
 
In the above statement, the void means that there is no return value expected from this native method call. The empty parentheses in the method name callToPLI() mean that there are no parameters being passed on the call to the native method.
2. Load the native library so that the native library will be loaded at execution time.

You can use the following Java statement to load the native library:
static {
  System.loadLibrary("hiFromPLI");
}
In this statement, the Java System method System.loadLibrary(...) is called to find and load the native library. The PL/I shared library, libhiFromPLI.so, will be created during the step that compiles and links the PL/I program.
3. Write the Java Main method.

The callingPLI class also includes a main method to instantiate the class and call the native method. The main method instantiates callingPLI and calls the callToPLI() native method. The complete definition of the callingPLI class, including all the points addressed above, is as follows:
public class callingPLI {
  public native void callToPLI();
  static {
     System.loadLibrary("hiFromPLI");
  }
  public static void main(String[] argv) {
     callingPLI callPLI = new callingPLI();
     callPLI.callToPLI();
     System.out.println("And Hello from Java, too!");
  }
}
 
Step 2: Compiling the Java program
Use the Java compiler to compile the callingPLI class into an executable form. You can use the following command:
javac callingPLI.java

Step 3: Writing the PL/I Program

The PL/I implementation of the native method looks much like any other PL/I subroutine.

Useful PL/I compiler options

The sample program contains a series of *PROCESS statements that define the important compiler options.
*Process Limits( Extname( 100 ) ) Margins( 1, 100 ) ;
*Process Display(Std) Dllinit Extrn(Short);
*Process LP(64) Default( ASCII IEEE );
Here is a brief description of them and why they are useful:
Extname(100)
Allows for longer, Java style, external names.
Margins(1,100)
Extending the margins gives you more room for Java style names and identifiers.
Display(Std)
Writes the "Hello World" text to stdout, not through WTOs. In the z/OS UNIX environment, WTOs would not be seen by the user.
Dllinit
Includes the initialization code needed for creating a DLL.
Extrn(Short)
EXTRNs are emitted only for those constants that are referenced.
Default(ASCII IEEE)
ASCII specifies that CHARACTER and PICTURE data is held in ASCII - the form in which it is held by Java.
IEEE specifies that FLOAT data is held in IEEE format - the form in which it is held by Java.
LP(64)
Tells the compiler to generate 64-bit code. LP(64) implies RENT.
Correct form of PL/I procedure name and procedure statement
 
The PL/I procedure name must conform to the Java naming convention in order to be located by the Java Class Loader at execution time. The Java naming scheme consists of three parts. The first part identifies the routine to the Java environment. The second part is the name of the Java class that defines the native method, and the third part is the name of the native method itself.

Here is a breakdown of PL/I procedure name Java_callingPLI_callToPLI in the sample program:
Java
All native methods resident in dynamic libraries must begin with Java.
_callingPLI
The name of the Java class that declares the native method.
_callToPLI
The name of the native method itself.
Note: There is an important difference between coding a native method in PL/I and in C. The javah tool, which is supplied with the JDK, generates the form of the external references required for C programs. When you write your native methods in PL/I and follow the rules above for naming your PL/I external references, performing the javah step is not necessary for PL/I native methods.

In addition, the following options must be specified in the OPTIONS option on the PROCEDURE statement:
  •    FromAlien
  •    NoDescriptor
  •    ByValue
The complete PROCEDURE statement for the sample program is as follows:
Java_callingPLI_callToPLI:
Proc( JNIEnv , MyJObject )
  External( "Java_callingPLI_callToPLI" )
  Options( FromAlien NoDescriptor ByValue );

JNI include file

The two PL/I include files that contain the PL/I definitions of the Java Native interface are ibmzjni.inc, which in turn includes ibmzjnim.inc. These include files are included with this statement:
%include ibmzjni;

The ibmzjni and ibmzjnim include files are provided in the PL/I SIBMZSAM data set.

The complete PL/I procedure

For completeness, here is the entire PL/I program that defines the native method:
*Process Limits( Extname( 100 ) ) Margins( 1, 100 ) ;
*Process Display(Std) Dllinit Extrn(Short);
*Process LP(64) Default( ASCII IEEE );
PliJava_Demo: Package Exports(*);

  Java_callingPLI_callToPLI:
  Proc( JNIEnv , MyJObject )
    External( "Java_callingPLI_callToPLI" )
    Options( FromAlien NoDescriptor ByValue );

    %include ibmzjni;
    Dcl myJObject Type jObject;
    Display('Hello from Enterprise PL/I!');

End;
Step 4: Compiling and linking the PL/I program

1. Compile the PL/I sample program with the following command:
pli -c hiFromPLI.pli

2. Link the resulting PL/I object deck into a shared library with this command:
c89 -Wl,”LP64,DLL” -o libhiFromPLI.so hiFromPLI.o

Ensure the lib prefix is included in the name of the PL/I shared library; otherwise, the Java class loader cannot find it.

Step 5: Running the sample program

Run the Java-PL/I sample program with this command:
java callingPLI

The output of the sample program is as follows:
Hello from Enterprise PL/I!
And Hello from Java, too!

The first line written from the PL/I native method. The second line is from the calling Java class after returning from the PL/I native method call.
Ensure the shared library location is added to LIBPATH, or referenced by the java.library.path property.

Attaching programs to an existing Java VM
You can use the Java invocation API to attach your program to an existing Java VM that was not created by your program.
When your PL/I application is running within an IMS JMP (Java messaging processing) region, IMS already created a Java VM. You can take the following steps to attach your program to the Java VM. Then, you can call any functions that you need through the Java native interface.

1. Locate the Java VM instance to attach your program to by using the JNI_GetCreatedJavaVMs API.

In this IMS environment, only one Java VM is created, so you ask for only one Java VM pointer to be returned. You can code the call as follows:
rc = JNI_GetCreatedJavaVMs( jvm_ptr, 1, nVMs );

jvm_ptr
Is a pointer to a Java VM and is declared in the IBMZJNI include file. Java returns the address of the Java VM in this variable when the call returns successfully.

nVMs
Is a fixed bin(31) variable that Java updates with the number of Java VM addresses when it returns. nVMs is 1 if the call is successful.

2. Acquire JNI environment pointer for the Java VM.

You can use the GetEnv function in the JNIInvokeInterface_ structure that is declared in the IBMZJNI include file:
rc = GetEnv( jvm_ptr, JNIEnv, JNI_VERSION_10 );

jvm_ptr
Is the pointer to the Java VM instance.

JNIEnv
Is a pointer that Java sets to the JNI environment pointer for the Java VM instance.

JNI_VERSION_10
Is a constant holding the value of an interface version number, which is also declared in the IBMZJNI include file.

Now that you have the JNI environment pointer for the Java VM, you can make calls to any of the JNI functions through the Java native interface.
Determining equivalent Java and PL/I data types

When you communicate with Java from PL/I, you need to match the data types between the two programming languages.  The following table shows the Java primitive types and their PL/I equivalents.

Java primitive types and PL/I native equivalents
Java type PL/I type Size in Bits
boolean jboolean 8, unsigned
byte jbyte 8
char jchar 16, unsigned
short jshort 16
int jint 32
long jlong 64
float jfloat 21
double jdouble 53
void jvoid n/a




 

[{"Type":"MASTER","Line of Business":{"code":"LOB36","label":"IBM Automation"},"Business Unit":{"code":"BU059","label":"IBM Software w\/o TPS"},"Product":{"code":"SSNVBF","label":"Runtimes for Java Technology"},"ARM Category":[],"Platform":[{"code":"PF025","label":"Platform Independent"}],"Version":"All Versions"}]

Document Information

Modified date:
22 November 2022

UID

ibm16608156