This sample program is a little different from the previous samples. In this sample PL/I invokes Java first, through the Java Invocation API, creating an embedded Java Virtual Machine (JVM). PL/I then calls a Java method passing it a string which the Java method then displays.
The PL/I sample program is named createJVM.pli and the Java method it calls is contained in javaPart.java.
Since this sample does not use a PL/I native method there is no need to declare one. Instead, the Java portion for this sample is just a simple Java method.
The javaPart class contains only one statement. This statement prints out a short 'Hello World...' from Java then appends the string that was passed to it from the PL/I program. The entire class is shown in Figure 86.
// Receive a string from PL/I then display it after saying "Hello"
public class javaPart {
public static void main(String[] args) {
System.out.println("From Java - Hello World... " + args[0]);
}
}The command to compile the Java code looks like this:
javac javaPart.java
Most of the information about writing the PL/I "Hello World" sample program applies to this program as well. However, since in this sample PL/I is calling Java, there are some additional points to consider.
Since in this sample the PL/I program is calling Java, the PL/I program is MAIN. There is no need to be concerned about the external name of this PL/I program because it is not referenced.
The complete procedure statement for the sample program looks like this:
createJVM: Proc Options(Main);
The two PL/I include files which contain the PL/I definitions of the Java Native interface are ibmzjni.inc which in turn includes ibmzjnim.inc. Even though in this sample PL/I is calling Java, these include files are still necessary. 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.
Since this PL/I sample program calls Java we need to prepare it to link to the Java library. The Java libraries are linked with XPLINK and the PL/I modules are not. PL/I can still link to and call XPLINK libraries but must use the PLIXOPT variable to specify the XPLINK=ON run-time option. This is what the declare of the PLIXOPT variable would look like:
Dcl PLIXOPT Char(40) Varying Ext Static Init( 'XPLINK(ON)'e );
For a description of using PLIXOPT, see z/OS Language Environment Programming Guide.
This PL/I sample program calls the Java Invocation API JNI_CreateJavaVM to create its own embedded JVM. This API requires certain structures to be set up and initialized correctly as shown in Figure 87. First, JNI_GetDefaultJavaVMInitArgs is called to get the default initilization options. Next, these default options are modified with the addition of the java.class.path information. Finally, JNI_CreateJavaVM is called to create the embedded JVM.
The complete PL/I program is shown in Figure 87. This sample PL/I program makes several calls through the JNI.
In this sample the reference to the Java object, a newly created JVM in this case, is not passed in but is instead returned from the call to the JNI_CreateJavaVM API. The PL/I program will use this reference to get information from the JVM. The first piece of information is the Class containing the Java method we wish to call. This Class is found by using the FindClass JNI function. The Class value is then used by the GetStaticMethodID JNI function to get the identity of the Java method that will be called.
Before we call this Java method we need to convert the PL/I string into a format that Java understands. The PL/I program holds the string in ASCII format. Java strings are stored in UTF format. In addition, Java strings are not really strings as PL/I programmers understand them but are themselves a Java class and can only be modified through methods. We create a Java string barry by using the NewStringUTF JNI function. This function returns a Java object called myJString that contains the PL/I string converted to UTF. Next we create a Java object array by calling the NewObjectArray JNI function passing it the reference to the myJString object. This function returns a reference to a Java object array containing the string we want the Java method to display.
Now the Java method can be called with the CallStaticVoidMethod JNI function and will then display the string passed to it. After displaying the string, the PL/I program destroys the embedded JVM with the DestroyJavaVM JNI function and the PL/I program completes.
The complete source of the PL/I program is in Figure 87.
*Process Limits( Extname( 100 ) ) Margins( 1, 100 );
*Process LangLVL( SAA2 ) Margins( 1, 100 ) ;
*Process Display(STD) Rent;
*Process Default( ASCII ) Or('|');
createJVM: Proc Options(Main);
%include ibmzjni;
Dcl myJObjArray Type jobjectArray;
Dcl myClass Type jclass;
Dcl myMethodID Type jmethodID;
Dcl myJString Type jstring;
Dcl myRC Fixed Bin(31) Init(0);
Dcl myPLIStr Char(50) Varz
Init('... a PLI string in a Java Virtual Machine!');
Dcl OptStr Char(1024) Varz;
Dcl myNull Pointer;
Dcl VM_Args Like JavaVMInitArgs;
Dcl myOptions Like JavaVMOption;
Dcl PLIXOPT Char(40) Varying Ext Static Init( 'XPLINK(ON)'e );
Display('From PL/I - Beginning execution of createJVM...');
/* Important difference between J1.3 and J1.4: */
/* J1.3 requires VM_Args.version = JNI_VERSION_1_2; */
/* J1.4 requires VM_Args.version = JNI_VERSION_1_4; */
VM_Args.version = JNI_VERSION_1_4;
myRC = JNI_GetDefaultJavaVMInitArgs( addr(VM_Args) );
OptStr = "-Djava.class.path=.:";
myOptions.theOptions = addr(OptStr);
VM_Args.nOptions = 1;
VM_Args.JavaVMOption = addr(myOptions);
/* Create the Java VM */
myrc = JNI_CreateJavaVM(
addr(JavaVM),
addr(JNIEnv),
addr(VM_Args) );
/* Get the Java Class for the javaPart class */
myClass = FindClass(JNIEnv, "javaPart");
/* Get static method ID */
myMethodID = GetStaticMethodID(JNIEnv, myClass,"main",
"([Ljava/lang/String;)V" );
/* Create a Java String Object from the PL/I string. */
myJString = NewStringUTF(JNIenv, myPLIStr);
myJObjArray = NewObjectArray(JNIEnv, 1,
FindClass(JNIEnv,"java/lang/String"), myJString);
Display('From PL/I - Calling Java method in new JVM from PL/I...');
Display(' ');
myNull = CallStaticVoidMethod(JNIEnv, myClass,
myMethodID, myJObjArray );
/* destroy the Java VM */
Display(' ');
Display('From PL/I - Destroying the new JVM from PL/I...');
myRC = DestroyJavaVM( JavaVM ); /* rc = -1 is OK */
end;Compile the PL/I sample program with the following command:
pli -c createJVM.pli
Link the resulting PL/I object deck with the Java library with this command:
c89 -o createJVM createJVM.o $JAVA_HOME/bin/classic/libjvm.x
Notice the reference to the $JAVA_HOME environment variable. This variable should point to the directory that your Java 1.4 product is installed in. For example, to set up this variable in your environment you would use the following command:
export JAVA_HOME="/usr/lpp/java/J1.4"
In this case the Java 1.4 product is assumed to be installed in /usr/lpp/java/J1.4.
Run the PL/I - Java sample program with this command:
createJVM
The output of the sample program will look like this:
From PL/I - Beginning execution of createJVM... From PL/I - Calling Java method in new JVM from PL/I... From Java - Hello World... ... a PLI string in a Java Virtual Machine! From PL/I - Destroying the new JVM from PL/I...