Table of Contents

Calling PL/I native methods from Java

  • What is java?
  • The Java Development Kit
  • Java applications and applets
  • Java native methods, what are they and why use them?
  • Why call a native language?
  • Benefits of calling a native language
  • A sample application for all platforms

  • How do you call PL/I from Java?
  • Creating a class with native methods
  • Generating C-style header and stub files
  • Linking your programs
  • Using a MAKEFILE
  • Writing and compiling your PL/I and Java programs
  • Calling your PL/I native methods
  • Summarizing what you have learned
  • References and Resources
  • Conversion table
  • Java string prototypes

  • Calling PL/I native methods from Java

    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.


    What is java?

    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 Kit

    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.

    Java applications and applets

    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 native methods, what are they and why use them?

    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.

    Why call a native language?

    Here are some possible reasons why you might want to call a native language:

    To be fair, here are some reasons why you might not want to call a native language:

    Benefits of calling a native language

    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.


    A sample application for all platforms

    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.


    How do you call PL/I from 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.

    1. Create a class with native method definitions or add them to an existing class.

    2. Generate C-style header and stub files using the javah tool.

    3. Write and compile your native language and Java programs.

    4. Link your native program into a native library.

    5. Call your native methods from your Java code.

    Creating a class with native methods

    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:

    public
    Everyone has access to this method (as opposed to 'private' which restricts usage)

    native
    A native method

    String
    A method which returns a Java string object

    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.

    Generating C-style header and stub files

    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;. Cellar
    
    You 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.

    Linking your programs

    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.

    Using a MAKEFILE

    A MAKEFILE uses the NMAKE tool which is available on many programming platforms and generally follows these steps:

    1. First, you create a file named yourFile.mak where yourFile is a name you choose.

    2. Next, you insert your make rules, compile commands, and link commands into the special NMAKE syntax.

    3. Finally, you invoke the makefile which performs the compile and link commands that are necessary.

    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.

    Writing and compiling your PL/I and Java programs

    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:

    PL/I compile-time options

    The significant compile-time options used include the following:

    LINKAGE

    For OS/2, DFT( LINKAGE( OPTLINK) ) which is the default linkage for PL/I and C/C++ on OS/2.

    For Windows, DFT( LINKAGE( CDECL) ) which is the default linkage for C/C++ on Windows

    BYVALUE

    All variables in Java are passed by reference, not by address.

    LANGLVL( SAA2 )

    The compiler accepts the PL/I language definition contained in the VisualAge PL/I Language Reference (OS/2 and Windows).

    LIMITS( EXTNAME( 31 ) )

    Use of this option allows longer external names.

    MARGINS( 1, 100 )

    Extending the margins gives you more room for longer OO-style names.

    MACRO

    Allows you to use the PL/I macro facility for conditional compilations.

    PL/I procedure names

    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.

    Variables passed to procedures

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

    Understanding Java string objects

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

    Calling your PL/I native methods

    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.


    Summarizing what you have learned

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


    References and Resources

    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/

    Conversion table

      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
    

    Java string prototypes

      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);