Calling PL/I native methods from Java
A sample application for all platforms
This chapter gives an overview of Java and explains why you might be interested in using it with PL/I. Because the actual implementation of calling PL/I from Java is subject to frequent change, these steps are described in a file named plijava.htm shipped with VisualAge PL/I. You need access to a browser to view the information in this file.
For OS/2, the browsable file is in the d:\ibmpli\help directory and the sample programs are in d:\ibmpli\samples, assuming you used d:\ibmpli\ to install the product.
For Windows NT, the browsable file is in the d:\ibmpliw\books directory and the sample programs are in d:\ibmpliw\samples, assuming you used d:\ibmpliw\ to install the product.
Java is an object-oriented programming language invented by Sun Microsystems and provides the most powerful way to make Internet documents interactive. Java was derived from C++, but is much more approachable. Java designers simplified it by removing memory management, pointers, and operator overloading. They also made Java more 'internet aware' by adding classes and methods for networking (TCP/IP, URLs, Sockets) and Applets.
The Java Development Kit 1.1.2 (most current version as of the printing of the document) has classes that support the Abstract Window Toolkit (AWT), I/O, utilities, and many other functions.
The Java compiler does not produce platform dependent machine code. Instead, it produces what are known as java byte codes in a Java .class file. These byte codes are not specific to any one processor or platform.
Java .class files are executed by an interpreter known as a Java virtual machine. The .class files are machine independent, meaning they can be compiled on one platform and run on any other platform that has a Java virtual machine. This is a great benefit to programmers who want to write a program once and then have it run on many platforms without altering the code.
Because Java is very new, it is still evolving. Java support on OS/2 first came out at the Java 1.0.2 level. The current support on OS/2 is for Java 1.1.1 which is generally available at the time this information was written. Sun Microsystems provides a Software Development Kit (SDK) that supports Java 1.1.3 on Windows platforms. There are significant changes between JDK 1.0.2 and JDK 1.1.3. You should always start with the newest version of Java available when developing Java applications.
The Java Development Kits come with a Java compiler (javac), a Java runtime, debugger and assorted tools. The PL/I sample program provide with VisualAge PL/I was developed using the OS/2 Java 1.1.1 and Sun Microsystems Windows Java Development Kits 1.1.2 and 1.1.3. These Java Development Kits are free.
Java programs have a suffix of .java. The Java compiler is case sensitive and requires that the source file name matches the class name it contains. For example, the class 'MyFirstJavaClass { }' is different than 'Myfirstjavaclass { }'. The source file for MyFirstJavaClass must be named MyFirstJavaClass.java. The compiler names the output file MyFirstJavaClass.class.
If you have more than one Java class in your source file, the Java compiler creates a separate .class file for each of the classes it finds.
A Java program can be either an application or an applet.
Java applications are self contained programs much like traditional PL/I programs. They contain one or more classes, few or many methods, and run on their own.
Java applets are designed to run in a browser environment. This makes them a good choice for internet or client/server-based applications. Applets operate with the following restrictions:
These restrictions are intended to protect your system from a 'misbehaving' applet.
Applets and applications also differ in their structure. Here are a couple of the more significant differences:
It is possible to create a Java program that can be run either as an applet or an application as long as it adheres to the applet restrictions.
You can run a Java application by entering a command like the one shown here:
java myApplication
The java command invokes the Java virtual machine and passes it the name of your Java program. This command is carried out based on the assumption that a file named myApplication.class exists somewhere on the CLASSPATH.
You run a Java applet by using one of the following:
For example, the following command would run your myApplet applet on OS/2 by invoking the java interpreter and running the file myApplet.class.
javapm myApplet
At the time this information is being written, Java Version 1.1 is so new that no browser currently supports it. Until one does, you can use javapm and appletviewer to mimic the browser environment.
There are many other issues and more information necessary to create and run Java programs. If you are interested in learning more about programming in Java, you might want to invest in a good Java book or set of online information.
Java is a fairly complete programming language, however, there could be situations in which you want to call a program written in another programming language. In Java, you do this with a method call to a native language, known as a native method.
Here are some possible reasons why you might want to call a native language:
For example, you might need to access a specific operating system feature or a piece of special hardware.
You could have an intensive series of calculations that need to be performed.
If you have skilled PL/I programmers, you want to get the most out of them.
Rather than move to a new language, it is in your best interest to keep using the code that has been valuable to you for years.
To be fair, here are some reasons why you might not want to call a native language:
For example, if your program calls a native method in a programming language that runs only on UNIX, you will not be able to move it to another platform.
Even if the programming language you choose is widely used, you still need to recompile your native program on each platform for which you deliver.
Fortunately, this is not such a problem with PL/I. You can use the PL/I macro facility to write your program using conditional code. This allows you to compile correctly on your target operating system, as illustrated by the Java PL/I sample application.
Portability is not a problem with the Java bytes portion of your product as it can be run 'as is' on any supported platform.
For example, an applet is restricted from executing native shared or load libraries on a client machine. This shortcoming can be overcome by designing your application to use native methods in the server part of the code, assuming a client/server environment.
An important benefit of using a native language is being able to leverage your current PL/I skills and PL/I program base. You can continue to use the highly-trained PL/I programmers in your shop to produce powerful applications as well as reuse your existing code.
You can develop a Graphical User Interface (GUI) using Java that will run on all of the supported platforms that call your native language. By combining a portable GUI and a set of powerful native methods, you are able to move your application from platform to platform.
As you spend time learning about Java through the sample application provided, you will learn how to connect your legacy code into the flexible world of the internet. As was mentioned in the introduction to this chapter, the methods used to create this sample application scenario are so dynamic that the steps are provided in an online document so we can provide updates as technology evolves.
Our example is a basic inventory-control program that has a Java-built GUI front end client that calls the workhorse PL/I engine through some native methods.
To see the sample PL/I program, named winecelr.pli, please refer to "winecelr.pli". The two PL/I procedures in this program, 'GetWine' and 'GetDet', are defined as native methods in the Cellar.java program. The native methods in Cellar.java are called from classes defined in the wineSamp.java java program. To see this Java program please refer to "wineSamp.java".
While this application is quite basic in nature, it illustrates what you need to know to hook a PL/I program to Java.
Java designers have created a scheme which allows you to call your native programming language from Java. Each of the following steps is discussed in detail as you proceed through this information.
The Cellar class is used for the sample application and is defined in a file of the same name with the extension java (Cellar.java). This class file has two native methods named GetWine and GetDet, each of which passes and returns some variables. The native object module is linked into a load library named Cellar. Here is what the complete Cellar.java file looks like: "Cellar.java".
The methods used in the file are described with 'public native String', defined as follows:
The GetWine method passes a Java string object and an integer while GetDet pass only a Java string object. More about Java string objects is presented later in the scenario.
The native load library, Cellar.dll for our example, is loaded when the Cellar class is loaded with the System.loadLibrary(..) call.
Java designers adopted the C calling convention for native programming languages. Therefore, Java treats all native programming languages (PL/I, COBOL, C, C++, etc.) the same making it more convenient to create PL/I modules that adhere to this calling convention.
To help native programmers to connect their code to Java, the Java designers provide a tool called 'javah'. This tool generates C-style header and stub files based on the definition of the native methods provided. For the sample application in this PL/I scenario, you invoke the tool to create "cellar.h". using this code:
javah -classpath p:\jdk1.1.3\classes.zip;. CellarYou invoke the tool to create "cellar.c". using this code:
javah -classpath p:\jdk1.1.3\classes.zip;. -stubs Cellar
Because both of these files are automatically generated and cryptic in nature, you should not have a need to (nor should you) alter them. The purpose of the files is to create a C-style stub through which your native language procedure can be called. The Java program calls the C-stub which in turn calls your PL/I procedure.
It is important to know which linkage convention to use for each platform. Since you are linking the PL/I program into a library with C stubs, the C and PL/I linking conventions must match. Not surprisingly, different linkages are used on OS/2 and on Windows.
On OS/2, the default linkage for both the PL/I and C/C++ compilers is OPTLINK. If you want to read more about OPTLINK, refer to the respective Programming Guides for those languages.
In PL/I, you specify linkage by using a suboption of the DEFAULT compile-time option, for example:
DFT( LINKAGE( OPTLINK ) )
For the Windows platform, you need to use the CDECL linkage option. The linkage specification in that case would be as follows:
DFT( LINKAGE( CDECL ) )
Each linkage convention 'decorates' the external reference of the procedure a little differently. You need to be aware of these differences when you set up your linking defaults. For example, in OPTLINK, the external reference to the Cellar_GetWine procedure would be 'Cellar_GetWine' while with CDECL it would be '_Cellar_GetWine'.
Thus far, there is a lot of information you need to know just to compile and link your Java and PL/I programs. One way to remember all of these details is to use a MAKEFILE.
A MAKEFILE uses the NMAKE tool which is available on many programming platforms and generally follows these steps:
On both OS/2 and Windows, you invoke your MAKEFILE the same way:
nmake /f yourFile.mak
If you are interested in makefile details, here are the makefiles for "OS/2". and for "Windows"
With a basic understanding of the development environment, you can start writing the programs that actually do the work.
The sample PL/I program consists of two procedures. The first procedure, GetWine, is called and passed a reference to a Java string object and an integer. This procedure converts the Java string object to a PL/I character string and evaluates it. Then, GetWine builds an answer set and returns it as another Java string object to the caller.
The second procedure also is passed a Java string object. The operation for GetDet is very similar to that for GetWine. GetDet converts and evaluates the passed string and returns another Java string object.
Although the sample PL/I program is quite basic, you should pay attention to the following:
The significant compile-time options used include the following:
For Windows, DFT( LINKAGE( CDECL) ) which is the default linkage for C/C++ on Windows
The sample application is written as a PL/I package and exports two procedures. The names of these procedures must adhere to the Java native method naming convention, which consists of two parts. The first part is the name of the Java class that defines it and the second part is the name of the native method itself.
Using this naming convention, the first procedure name becomes 'Cellar_GetWine'. For this procedure, 'Cellar' is the name of the class that defines it and 'GetWine' is the native method name inside the class. The second procedure is named 'Cellar_GetDet' for the same reasons.
The procedure, 'Cellar_GetWine' is called from wineSamp.java and is passed two parameters explicitly. Java also always passes a 'this' pointer as the first parameter. The native program never does anything with this first parameter but must account for it in the receiving procedure.
Consider this sample call from a Java program:
string2 = myCellar.GetWine( string1, flag );
The procedure statement of the receiving native PL/I program would look like this:
Cellar_GetWine: Proc( this , javaInPtr, flag )
The first variable received is the Java 'this' pointer, which is declared but never used. The second parameter is a pointer to a Java string object, and the third is an integer. In the PL/I program, the receiving parameters would be declared as follows:
Dcl this pointer byvalue; Dcl javaInPtr pointer byvalue; Dcl flag fixed bin(31) byvalue;
For a mapping between Java and PL/I data types, refer to "Conversion table".
In Java, the character string is not a primitive data type. Instead, Java string literals are implemented as objects of the string class. In Java, you manipulate strings through methods, not directly like you do in PL/I.
In order for a PL/I program to manipulate a Java string object, a transformation must occur. This transformation is made with a call to a Java runtime procedure:
stringPtr = makeCString( javaInPtr );
A Java runtime routine, 'makeCString', is called and passed the pointer to the Java string object. The routine, in turn, returns a pointer to a buffer where the Java string object is unpackaged and becomes usable by the PL/I program.
Instead of examining the PL/I sample application in a lot of detail, you should focus in its two procedures. Each procedure receives a Java string object, transforms it, evaluates it, and builds a return string containing the answer set. The return string is then transformed back into a Java string object and a reference to this string object is returned to the Java program. The source for the "winecelr.pli". program is provided for your reference.
The PL/I string is transformed into a Java string object and returned to the calling Java program with the following PL/I statements:
returnDetPtr = makeJavaString(retList, length(retList)); return(returnDetPtr);
The Java runtime routine, 'makeJavaString', is called to perform the transformation. You can view the prototypes for these two routines in "Java string prototypes".
The actual calls to the PL/I native methods are fairly uncomplicated considering all of the preparation that must be done to make them work.
The sequence of Java statements to call the first PL/I procedure, 'Cellar_GetWine', from the Java program looks like this:
string1 = myChoice.getSelectedItem(); String string2 = new String(" "); string2 = myCellar.GetWine( string1, flag );
The idea of this call is to get the selected value from a listbox (a wine variety in this application), pass it to the PL/I routine which then returns all the wines in our wine cellar of that variety.
The first java statement gets the selected item from the listbox myChoice and loads it into a Java string object string1. The next instruction creates a new Java string object string2 to hold the Java string object returned from the PL/I routine. The final statement, calls the 'GetWine' method and passes it the Java string object containing the selected wine variety string1 and an unrelated integer value flag0.
You might notice that the last line does not contain any reference to the 'Cellar' class which contains the definition for the 'GetWine' method. The connection between 'GetWine' and the 'Cellar' class was made previously in a statement similar to this example:
Cellar myCellar = new Cellar();
Here, the 'Cellar' class is instantiated into an object named 'myCellar' which the sample application uses.
The second call to GetDet(....) is very similar to the first, therefore no additional explanation is required.
You have just scratched the surface regarding the Java programming language. Perhaps you now have a greater understanding of the value of calling a native programming language from Java. You have seen the steps to create, implement, and use a Java native method to call a PL/I program.
While this sample application illustrates the basics of using Java native methods, there is still much more to learn. A more interactive GUI, some networking, graphics, and animation could easily be added to enhance this application. Hopefully, you now have the foundation you need to get started into the world of the internet and intranet. Once you take the first step, you are only limited by your imagination (or that of your employer).
Places to download the Java Development kits for free:
IBM Java Development Kit 1.1 for OS/2:
http://service.boulder.ibm.com/asd-bin/doc/en_us/java111/f-feat.htm
Sun Microsystems Java Development Kit 1.1.3 for Windows:
http://java.sun.com/products/jdk/1.1/
Java Type C Type PL/I Type --------- --------- --------- boolean long fixed bin(31) byvalue byte long fixed bin(31) byvalue short long fixed bin(31) byvalue int long fixed bin(31) byvalue float float real float bin(21) byvalue double double real float bin(53) byvalue char long fixed bin(31) unsigned byvalue
dcl makeJavaString entry( char(*) byaddr nonasgn, fixed bin(31) byvalue ) external( 'makeJavaString' ) %IF Samp_RunsOn = 'OS2' /* If execution environment is OS/2 */ %THEN %DO; options( nodescriptor linkage( optlink ) ) %END; %ELSE %DO; options( nodescriptor linkage( cdecl ) ) %END; returns( pointer byvalue ); dcl makeCString entry(pointer byvalue) external('makeCString') %IF Samp_RunsOn = 'OS2' /* If execution environment is OS/2 */ %THEN %DO; options( nodescriptor linkage(optlink) ) %END; %ELSE %DO; options( nodescriptor linkage( cdecl ) ) %END; returns(pointer byvalue);