RPG and Java - IBM - RPG

Introduction to Java and RPG

The Java programming language is a high-level object-oriented language developed by Sun Microsystems. Java programs can be developed using the VisualAge® for Java component of WebSphere Development Studio for iSeries.

In object-oriented programming, a ″method″ is a programmed procedure that is defined as part of a ″class″, which is a collection of methods and variables. Java methods can be called from your RPG program. While most Java methods are written in Java, a method can also be written in another high-level language, such as RPG. This is known as a ″native method″. This section includes information on calling Java methods from RPG and on writing RPG native methods.

The Object Data Type and CLASS Keyword
Fields that can store objects are declared using the O data type. To declare a field of type O, code O in column 40 of the D-specification and use the CLASS keyword to provide the class of the object. The CLASS keyword accepts two parameters:

CLASS(*JAVA:class_name)
*JAVA identifies the object as a Java object.Class_name specifies the class of the object. It must be a character literal or named constant, and the class name must be fully qualified. The class name is case sensitive.

For example, to declare a field that will hold an object of type BigDecimal:

D bdnum S O CLASS(*JAVA:’java.math.BigDecimal’)

To declare a field that will hold an object of type String:

D string S O CLASS(*JAVA:’java.lang.String’)

Note that both class names are fully qualified and that their case exactly matches that of the Java class.

Fields of type O cannot be defined as subfields of data structures. It is possible to have arrays of type O fields, but pre-runtime and compile-time tables and arrays of type O are not allowed.

Prototyping Java Methods
Like subprocedures, Java methods must be prototyped in order to call them correctly. The ILE RPG compiler must know the name of the method, the class it belongs to, the data types of the parameters and the data type of the returned value (if any), and whether or not the method is a static method.

The extended EXTPROC keyword can be used to specify the name of the method and the class it belongs to. When prototyping a Java method, the expected format of the EXTPROC keyword is:

EXTPROC(*JAVA:class_name:method_name)

Both the class name and the method name must be character constants. The class name must be a fully qualified Java class name and is case sensitive. The method name must be the name of the method to be called, and is case sensitive.

Use *JAVA when creating a prototype for either a method written in Java or a native method written in RPG. Use the STATIC keyword to indicate that a method is static.

Java and RPG Definitions and Data Types:
The data types of the parameters and the returned value of the method are specified in the same way as they are when prototyping a subprocedure, but the data types actually map to Java data types. The following table shows the mappings of ILE RPG data types to and from Java data types.

Java and RPG Definitions and Data TypesJava and RPG Definitions and Data Types

Notes:

  1. When a Java byte type is converted to or from a character (1A) data type, ASCII conversion occurs. When a Java byte type is converted to or from an integer (3I) data type, ASCII conversion does not occur.
  2. For arrays of any type in Java, you can declare an array of the equivalent type in RPG. However, note that you cannot use an array of character length greater than 1 or UCS-2 length greater than 1 data types.
  3. For UCS-2 length greater than 1 and character length greater than 1 data types, the VARYING keyword is allowed. In general, it’s recommended to use the VARYING keyword, since Java byte[] and char[] cannot be declared with a fixed length.
  4. For RPG array data types, OPTIONS(*VARSIZE) should normally be coded for array parameters, since Java arrays cannot be declared with a fixed length.

Zoned, Packed, Binary, and Unsigned data types are not available in Java. If you pass a Zoned, Packed, Binary, or Unsigned field as a parameter, the compiler will do the appropriate conversion, but this may result in truncation and/or loss of precision.

When calling a method, the compiler will accept arrays as parameters only if the parameter is prototyped using the DIM keyword.

If the return value or a parameter of a method is an object, you must provide the class of the object by coding the CLASS keyword on the prototype. The class name specified will be that of the object being returned or the parameter being passed. (Use the EXTPROC keyword to specify the class of the method being called.)

If the method being called is a static method, then you must specify the STATIC keyword on the prototype. If the method is a constructor, you must specify *CONSTRUCTOR as the name of the method.

In Java, the following data types can only be passed by value:

boolean
byte
int
short
long
float
double

Parameters of these types must have the VALUE keyword specified for them on the prototype.

Note that objects can only be passed by reference. The VALUE keyword cannot be specified with type O. Since arrays are seen by Java as objects, parameters mapping to arrays must also be passed by reference. This includes character and byte arrays. The CONST keyword can be used.

Examples of Prototyping Java Methods: This section presents some examples of proto typing Java methods.

Example:The Java Integer class contains a static method called toString, which accepts an int parameter, and returns a String object. It is declared in Java as follows:

static String Integer.toString(int)

This method would be prototyped as follows:
Examples of Prototyping Java Methods

The EXTPROC keyword identifies the method as a Java method. It also indicates that the method name is ’toString’, and that it is found in class ’java.lang.Integer’. The O in column 40 and the CLASS keyword tell the compiler that the method returns an object, and the class of that object is ’java.lang.String’.

The STATIC keyword indicates that the method is a static method, meaning that an Integer object is not required to call the method.

The data type of the parameter is specified as 10I, which maps to the Java int data type. Because the parameter is an int, it must be passed by value, and the VALUE keyword is required.

Example: The Java Integer class contains a static method called getInteger, which accepts String and Integer objects as parameters, and returns an Integer object. It is declared in Java as follows:
static Integer Integer.getInteger(String, Integer)

This method would be prototyped as follows:
example2

This method accepts two objects as parameters. O is coded in column 40 of the D-specification and the CLASS keyword specifies the class of each object parameter. Because both parameters are input-only, the CONST keyword is specified.

Example: The Java Integer class contains a method called shortValue, which returns the short representation of the Integer object used to invoke the method. It is declared in Java as follows:

short shortValue()

This method would be prototyped as follows:
example3

The STATIC keyword is not specified because the method is not a static method. The method takes no parameters, so none are coded. When you call this method, you will specify the Integer instance as the first parameter. The returned value is specified as 5I, which maps to the Java short data type.

Example: The Java Integer class contains a method called equals, which accepts an Object as parameter and returns a boolean. It is declared in Java as follows:

boolean equals(Object)

This method would be prototyped as follows:
example4

The returned value is specified as N, which maps to the Java boolean data type. Because this is not a static method, a call to this method will have two parameters with the instance parameter coded first.

Calling Java Methods from ILE RPG
This section describes how to call Java methods from ILE RPG programs.

If the method is not a static method, then it is called an ″instance method″ and an object instance must be coded as an extra first parameter in order to call the method. For example, if an instance method is prototyped with one parameter, you must call it with two parameters, the first being the instance parameter.

The following steps describe the call from ILE RPG to a Java method:

  1. Java methods can be called using existing operation codes CALLP (when no return value is expected) and EVAL (when a return value is expected). When your RPG procedure attempts to make call to a Java method, RPG will check to see if the Java Virtual Machine (JVM) has been started. If not, RPG will start the JVM for you. It is also possible to start JVM yourself using the JNI function described in “Creating the Java Virtual Machine (JVM)”
  2. If you are using your own classes (or any classes outside the normal java.xxx classes), be sure to have your CLASSPATH environment variable setup before you call any Java methods. When RPG starts up the JVM for you, it will add the classes in your CLASSPATH environment variable to the standard classpath, so when you use your own classes, Java will be able to find them. Set the CLASSPATH environment variable interactively like this:
    ===>ADDENVVAR ENVVAR(CLASSPATH) VALUE(’/myclasses/:/xyzJava/classes/’) The directories must be separated by colons.
  3. Normally, Java does its own garbage collection, detecting when an object is no longer needed. When you create objects by calling Java constructors from your non-native RPG procedure, Java has no way of knowing that the object can be destroyed, so it never destroys them. You can enable garbage collection for several objects at once by calling the JNI functions described in “Telling Java to free several objects at once”. If you know you are not going to need an object any more, you should tell this to Java by calling the JNI function described in “Telling Java you are finished with a temporary object”.

CAUTION: Since Java uses threads, the THREAD(*SERIALIZE) keyword must be coded in all modules that interact with Java. RPG relies heavily on static storage even in subprocedures that apparently only use automatic storage. THREAD(*SERIALIZE) is necessary to ensure the correct handling of this static storage. This applies not only to modules that contain calls to Java methods, but also to any modules that might be called during interactions with Java. See “Additional RPG Coding for Using Java” for more information about the various JNI functions.

Example In this example, the goal is to add two BigDecimal values together. In order to do this, two BigDecimal objects must be instantiated by calling the constructor for the BigDecimal class, fields must be declared to store the BigDecimal objects, and the add() method in the BigDecimal class must be called.

RPG Code Example Calling BigDecimal Java Class
RPG Code Example Calling BigDecimal Java Class

Here is the code that does the call.

code that does the call.

Example
This example shows how to perform a TRIM in Java by using the trim() method as an alternative to the ILE RPG %TRIM built-in function. The trim() method in the String class is not a static method, so a String object is needed in order to call it.

RPG Code Example Using trim() Java Method
RPG Code Example Using trim() Java Method

The call is coded as follows:

RPG Call to the String constructor
RPG Call to the String constructor

Static methods are called in the same way, except that an object is not required to make a call. If the getBytes() method above was static, the call would look like the example below.

C EVAL fld = getBytes()

If the method does not return a value, use the CALLP operation code.

Creating Objects
In order to call a non-static method, an object is required. The class of the object must be the same as the class containing the method. You may already have an object available, but you may sometimes need to instantiate a new object. You do this by calling a class constructor. A class constructor is neither a static method nor an instance method, and therefore it does not need an instance parameter. The special method name *CONSTRUCTOR is used when prototyping a constructor.

For example, class BigDecimal has a constructor that accepts a float parameter.

This constructor would be prototyped as follows:

Creating Objects

Note that the parameter must be passed by value because it maps to the Java float data type.

You would call this constructor like this:

Creating Objects

The class of the returned object is the same as the class of the constructor itself, so the CLASS keyword is redundant for a constructor, but it may be coded.

Calling methods in your own classes
When you use your own Java classes, the class that you specify in the EXTPROC and CLASS keywords is simply the name of the class. If the class is part of a package, you include the package information in the keywords. For example, consider the following two classes:

Calling methods in your own classes

If the Simple class file is /home/myclasses/Simple.class, you would specify the directory /home/myclasses in your CLASSPATH environment variable, and you would specify ’Simple’ as the class name in your RPG keywords.

If the PkgClass class file is /home/mypackages/MyPkg/PkgClass.class, you would specify the directory /home/mypackages (the directory containing the package) in your CLASSPATH environment variable, and you would specify ’MyPkg.PkgClass’ (the package-qualified Java class) as the class name in your RPG keywords.

The class name for your RPG keywords is the same name as you would specify in your import statements in your Java classes. You use the CLASSPATH environment variable to specify the location of the class files, or the location of the directory containing the package.

Note: Note: If you have classes in a jar file, you specify the jar file itself in your classpath.
===> ADDENVVAR CLASSPATH ’/home /myclasses: /home /mypackages: /home /myjarfiles /j1.jar’

Creating an RPG prototype for a Java method in a package
Creating an RPG prototype for a Java method in a package

Controlling how the Java Virtual Machine is set up
When RPG starts the Java Virtual Machine (JVM), there are several options that control how the JVM is started. See the Java System Properties section in the iSeries Information Center.

  • You can place these options in the SystemDefaults.properties file.
  • You can use the CLASSPATH environment variable to specify the classpath.
  • You can place these options in an environment variable called QIBM_RPG_ JAVA_ PROPERTIES. Any options placed in this environment variable will override the options in the System Defaults .properties file. If you specify the java. class. path option in this environment variable, and you also specified the CLASS PATH environment variable, it is undefined which value will take precedence for the classpath.

To specify options in the QIBM_RPG_JAVA_PROPERTIES environment variable, you code the options in a string, one after the other, separated by any character that does not appear in any of the options. Then you end the string with the separator character. For example, if you want to specify the options java.version=1.4 os400.stderr=file:stderr.txt then you would add the environment variable using the following command:
ADDENVVAR ENVVAR(QIBM_RPG_JAVA_PROPERTIES) VALUE(’-Djava.version=1.4;-Dos400. stderr=file :stderr.txt;’)

If the options string is not valid, Java may reject one of the options. Message JVAB55A will appear in the joblog indicating which option was not valid. If this happens, RPG will try to start the JVM again without any of the options, but still including the java.class.path option if it came from the CLASSPATH environment variable

RPG Native Methods
To define an RPG native method, you code the prototype the same way as you would code the prototype for an ordinary Java method. Then, you write the RPG subprocedure normally. You must code the EXPORT keyword on the Procedure-Begin Specification for the native method.

You must have your native methods in a service program in your library list. In your Java class that is calling your native methods, you must have a static statement like this:

This will enable Java to find your native methods. Aside from adding *JAVA and the class to the EXTPROC keyword for the prototype of a native method, you write your native method like any subprocedure.This is an example of a Java class that calls a native method.

CAUTION: If you are using environment variables to control how the JVM is started, you must be sure that the environment variables exist in the job before any RPG programs call Java methods. If you use ADDENVVAR LEVEL(*SYS), the environment variable will be added at the system level, and by default, every job will start with that environment variable set. If you do this, be sure that the classpath includes all the directories containing the Java classes that may be needed by any application on the system.

Java Class Calling a Native Method
Java Class Calling a Native Method

This is a prototype of an RPG native method.

RPG Native Method Prototype
RPG Native Method Prototype

The native method itself is coded just like any subprocedure. This is an example of a native method coded in RPG.

Native Method Coded in RPG
Native Method Coded in RPG

Java calls your service program from the default activation group. If your service program is created with activation group *CALLER, it will run in the default activation group. This can sometimes cause problems:

  • If you are debugging your native methods, and you want to make a change to the code, you will have to sign off and sign back on again before Java will see the new version.
  • If you are calling other procedures in the service program from other RPG code that is not running in the default activation group, then you will not be able to share any global variables between the ″ordinary procedures″ and the native methods. This scenario can arise if a procedure in your RPG service program sets up some global variables, and then calls a Java class which then calls a native method in that service program. Those native methods will not see the same data that the first procedure set up.

If you create any Java objects in your native methods, by default they will be destroyed by Java when the native method returns. If you want the object to be available after the native method returns (for example, if you want to use it from another native method later), then you must tell Java that you want to make a global reference, by calling the JNI wrapper procedure getNewGlobalRef . When you are finished with the global reference, you will call JNI wrapper procedure free Global Ref, so Java can reclaim the object. See “Telling Java you want an object to be permanent” and “Telling Java you are finished with a permanent object” for more information about these wrapper procedures.

If your RPG native method ends abnormally with an unhandled exception, the RPG compiler will throw an exception to Java. The exception is of class java.lang.Exception, and has the form RPG nnnnn, where nnnnn is the RPG status code.

Getting the Instance Parameter in Non-Static Native Methods
When a non-static native method is called, one of the parameters that Java passes to the native method is the object that the method applies to. This is called the ″instance parameter″, referred to as ″this″ in a Java method. Within the native method itself, you can use the built-in function %THIS to get the instance parameter. You do not code this parameter in your Procedure Interface.

Passing Character Parameters from Java to Native Methods
You have two choices when dealing with character parameters:

  • If you want your Java code to be a simple as possible, define the parameter as a String in your Java native method declaration. Your RPG code would have to retrieve the value of the string itself (see “Using String Objects in RPG”).
  • If you want the character data to be immediately available to your RPG program, code the parameter in the Java native method declaration as a byte array or a char array, and code it in your RPG prototype as a character field, UCS-2 field, or a Date, Time or Timestamp. That way, RPG will handle the conversion for you.

Using String Objects in RPG: If you have a String object in your RPG code, you can retrieve its length and contents using the code.

Retrieving String object length and contents from Java
Retrieving String object length and contents from Java

You can define the returned value from the getBytes method as character data of any length, either varying or non-varying, choosing the length based on your own knowledge of the length of data in the Java String. You can also define the return value as a Date, Time or Timestamp, if you are sure that the String object will have the correct format.

Alternately, you can retrieve the string value as a UCS-2 value, by calling the getChars method instead of getBytes.

Coding Errors when calling Java from RPG

Failure to free Java resources
When you create a Java object by calling a constructor, or by calling a method that returns an object, that object will remain in existence until it is freed. It is freed when:

  1. The RPG program calls a JNI function to free the object (see “Additional RPG Coding for Using Java”).
  2. When the native method returns, if the object was created during a call from Java to a native method.
  3. When the JVM ends.

If the RPG procedure calling the Java method is not itself an RPG native method, and the RPG procedure does not take care to free objects it has created, then the job may eventually be unable to create any more objects.

Consider the following code fragment:

It appears that this code is taking care to free the object, but in fact this code creates two objects. The first object is created by the called to newString(), and the second is created by the call to trim(). Here are two ways to correct this code fragment:

  1. By freeing several objects at once:
  2. By keeping track of all objects used, and freeing them individually:

Another problem can be created by calling Java methods as parameters to other Java methods. In the following example, the program is creating a BigDecimal object from the constructor that takes a String parameter:

The problem with this code is that a String object has been created for the parameter, but it can never be freed by the RPG procedure. This problem can be corrected by calling begin Obj Group() before the RPG code that calls Java and calling endObjGroup() after, or by coding as follows:

Using objects that no longer exist
If you have static Object variables in your native method (STATIC keyword on the definition), or your native method uses static global Object variables (variables declared in the main source section), then the Object variables will retain their values between calls to the native method. However, by default, Java will free any objects created during a call to a native method. (“Additional RPG Coding for Using Java” see how to prevent Java from freeing objects.)

An RPG ″Object″ is really a numeric object reference. When a Java object is freed, the numeric object reference can be reused. If the RPG native method refers to a static Object variable that has not been explicitly protected from being freed, one of two things can happen:

  1. The object reference may be invalid, if the numeric object reference has not been reused.
  2. The object reference may have been reused, but since it refers to a different object, any attempt to use it in the RPG native method will probably be incorrect.

To prevent problems with attempting to reuse objects illegally, the RPG programmer may do one or more of the following:

  • Avoid declaring any Object variables in static storage. Instead, declare all Object variables in local storage of subprocedures, without using the STATIC keyword.
  • Before returning from a native method, explicitly set all static object references to *NULL.
  • Upon entering a native method, explicitly set all static object references to some initial values.

Additional RPG Coding for Using Java
When you are using ILE RPG with Java, there are some functions normally handled by Java that must be handled by your RPG code. The RPG compiler takes care of some of these for you, but you must handle some of them yourself. This section shows you some sample RPG wrappers to do this work, explains how and when to call them, and suggests how to handle JNI exceptions.

The module that you create to hold these JNI wrapper functions should begin with the following statements:

H =thread(*serialize) H nomain /define OS400_JVM_12 /copy qsysinc/qrpglesrc,jni

The following RPG wrappers for JNI functions are described:

  • “Telling Java to free several objects at once”
  • “Telling Java you are finished with a temporary object”
  • “Telling Java you want an object to be permanent”
  • “Telling Java you are finished with a permanent object”
  • “Creating the Java Virtual Machine (JVM)”
  • “Obtaining the JNI environment pointer”

Telling Java to free several objects at once
You can free many local references at once by calling the JNI function PushLocalFrame before a section of RPG code that uses Java and then calling PopLocalFrame at the end of the section of RPG code. When you call PopLocalFrame, any local references created since the call to PushLocalFrame will be freed. For more information about the parameters to these JNI functions,

Telling Java to free several objects at once

Telling Java to free several objects at once

Note: You need the JNI environment pointer (described in “Obtaining the JNI environment pointer”) to call this wrapper.

Telling Java you are finished with a temporary object
If you have created an object using a Java constructor, or if you have called a Java method that returned an object to you, this object will only be available to be destroyed by Java’s garbage collection when it knows you do not need the object any more. This will happen for a native method when the native method returns, but it will never happen for a non-native RPG procedure, unless you explicitly inform Java that you no longer need the object. You do this by calling the RPG wrapper procedure freeLocalRef.

CALLP freeLocalRef (JNIEnv_P : string);

It contains the sample source code for freeLocalRef.

Source Code for freeLocalRef
Source Code for freeLocalRef

Note: You need the JNI environment pointer (described in “Obtaining the JNI environment pointer”) to call this wrapper.

Telling Java you want an object to be permanent
If you have a reference to a Java object that was either passed to you as a parameter or was created by calling a Java method or constructor, and you want to use that object after your native method returns, you must tell Java that you want the object to be permanent, or ″global″. Do this by calling the RPG wrapper procedure getNewGlobalRef and saving the result in a global variable.

EVAL globalString = getNewGlobalRef (JNIENV_P : string);

It contains the sample source code for getNewGlobalRef.

Source Code for getNewGlobalRef
Source Code for getNewGlobalRef

Telling Java you are finished with a permanent object
If you have created a global reference, and you know that you no longer need this object, then you should tell Java that as far as you are concerned, the object can be destroyed when Java next performs its garbage collection. (The object will only be destroyed if there are no other global references to it, and if there are no other references within Java itself.) To tell Java that you no longer need the reference to the object, call the RPG wrapper procedure freeGlobalRef . CALLP freeGlobalRef (JNIEnv_P : globalString);

This contains sample source code for freeGlobalRef.

This contains sample source code for freeGlobalRef.

Note: You need the JNI environment pointer (described in “Obtaining the JNI environment pointer” below) to call this wrapper.

Creating the Java Virtual Machine (JVM)
If the JVM has not already been created when your RPG code is ready to call a Java method, RPG will create the JVM for you, using the default classpath plus the classpath in your CLASSPATH environment variable. However, if you want to create the JVM yourself.

Obtaining the JNI environment pointer
If you need to call any JNI functions,use the /COPY file JNI from QSYSINC /QRPGLESRC. Most of the JNI functions are called through a procedure pointer. The procedure pointers are part of a data structure that it itself based on a pointer called the ″JNI environment pointer″. This pointer is called JNIEnv_P in the JNI /COPY file. To obtain this pointer, call the JNI wrapper procedure getJniEnv. EVAL JNIEnv_P = getJniEnv();

This contains sample source code for getJniEnv.

Obtaining the JNI environment pointer

Obtaining the JNI environment pointer

Obtaining the JNI environment pointer

Obtaining the JNI environment pointer

Obtaining the JNI environment pointer

Obtaining the JNI environment pointer

Handling JNI Exceptions
In ILE RPG, an exception causes an exception message to be signaled. Programs do not need to check explicitly for exceptions; instead, you can code exception handlers to get control when an exception occurs. You only have to handle JNI exceptions yourself when you are making your own JNI calls. When a call to a JNI function results in an unhandled Java exception, there is no accompanying exception message. Instead, the JNI programmer must check whether an exception occurred after each call to a JNI function.

This is done by calling the ExceptionOccurred JNI function, which returns a Java Exception object (or the Java null object which has a value of 0 in the JNI). Once you have determined that an exception has occurred, the only JNI calls you can make are ExceptionClear and ExceptionDescribe. After you have called ExceptionClear, you are free to make JNI calls again. If you make a non-exception JNI call before calling ExceptionClear, the exception will disappear, and you will not be able to get any further details. RPG always converts a JNI exception into an RPG exception (it signals one of the RNX030x messages, depending on the RPG function that was being done at the time).

Tip! You may want to include this type of exception-handling code in your versions of the JNI wrapper procedures above.

Additional Considerations Common Runtime Errors
The compiler will not attempt to resolve classes at compile time. If a class cannot be located at run time, a runtime error will occur. It will indicate that an Unresolved Link Exception object was received from the Java environment.

The compiler does no type checking of parameters at compile time. If there is a conflict between the prototype and the method being called, an error will be received at run time.

Debugging Hints
A Java object is viewed as an object reference in RPG. This object reference is an integer value, which behaves like a pointer. Normal object references are positive values, assigned in increasing order from 1. Global references, which can be created using JNI function NewGlobalRef , are negative values. These values are assigned in increasing order from the smallest negative number (-2147483647).

Normally, these values are not visible within the RPG code. However, this information may be useful when debugging RPG code.

Creating String objects in RPG
If you need a String object to pass to a Java method, you can create it like this:

Creating String objects in RPG

Getting information about exceptions thrown by called Java methods
When RPG calls a Java method that ends with an exception, RPG handles the Java exception and signals escape message RNX0301. This message has the string value of the Exception, but it does not have the trace information that is normally available when Java calls a method that ends with an exception.

If you want to see the Java exception trace information, do the following:

  1. ADDENVVAR ENVVAR(QIBM_USE_DESCRIPTOR_STDIO) VALUE(’Y’)
  2. Note: This step must be done before the JVM is started.

  3. Ensure that the os400.stderr option in your SystemProperties.default file is set to file:myfilename, for example os400.stderr=file:/home/mydir/stderr.txt. See “Controlling how the Java Virtual Machine is set up”.
  4. Note: This step must be done before the JVM is started.

  5. ADDENVVAR ENVVAR(QIBM_RPG_JAVA_EXCP_TRACE) VALUE(’Y’)
  6. Note: This step can be done at any time. To stop the exception trace being done by RPG, you can remove the environment variable, or set it to a value other than ’Y’.

  7. After the exception has occurred, the trace information will be in the file that you specified in the os400.stderr option.

Advanced JNI Coding
The RPG IV compiler support for calling Java methods and for writing RPG native methods hides almost all the JNI coding from the RPG programmer. However, RPG’s support is not necessarily the most efficient. For example, it always converts arrays between RPG and Java on calls and on entry and exit from native methods, but you may want to handle your own array conversions to improve performance.

The RPG support only gives you access to Java methods. If you want to access the fields in a class, you would have to add ″get″ and ″set″ methods to the Java class, or do JNI coding (see “Accessing Fields in Java Classes”).

Figure is an example of a JNI call in RPG.

Advanced JNI Coding

Note that the pointers JNIEnv_P and jvalue_P are defined in the JNI /COPY file.

Converting Java Character Data
In Java, character data is ASCII rather than EBCDIC, so you will have to ensure that class names, method names, and field names are in ASCII for calls to JNI functions like FindClass. Character data that comes from Java is ASCII. To use it in your RPG program, you will probably want to convert it to EBCDIC. The RPG compiler handles these conversions for you, but if you are making the JNI calls yourself, you will have to do the conversions between ASCII and EBSDIC.

Accessing Fields in Java Classes
RPG only supports calling Java methods; it does not support accessing Java fields. Normally, fields can be accessed through ″get″ and ″set″ methods, but it is also possible to access fields using JNI calls. Here is an example showing JNI calls necessary to access the fields of a Java class or object.

Note: This example is intended to be an example of using the JNI. It is not intended to be a recommendation to access fields directly rather than using ″get″ and ″set″ methods.

Using JNI to Access Fields of Java Classes and Objects
Using JNI to Access Fields of Java Classes and Objects

Calling Java Methods Using the JNI Rather than RPG *JAVA Prototypes
The first three parameters are always the same:

  1. the JNI environment pointer
  2. the object (for instance methods) or the class (for static methods)
  3. the methodC The method-specific parameters are coded after these three parameters, in one of three different ways.
    For example, if the method does not return a value (the return type is ″void″),

CallVoidMethod:
Choose this way if you are going to call the same method many times, since it makes the method very easy to call. This expects the parameters to be passed normally. To call this JNI function, an RPG programmer would copy the CallVoidMethod prototype from the JNI /COPY file, and code additional parameters. For example,

CallVoidMethod

CallVoidMethodA:
Choose this way if you do not want to create a separate prototype for calling a method. This expects an array of jvalue structures, with each element of the array holding one parameter.This is an example of this.

CallVoidMethodV:
Do not use this in RPG code. It expects a C construct that is extremely awkward to code in RPG.
The actual function to call depends on the type of the return value. For example, if the method returns an integer, you would use CallIntMethodA. To get the class and methodID parameters for these functions, use the FindClass and GetMethodID or GetStaticMethodID.

Note: When calling the JNI directly, the class names must be specified with a slash (/) rather than a period (.) as the separator. For example, use ’java/lang/String’ rather than ’java.lang.String’.

Calling RPG programs from Java using PCML
An RPG program or procedure can be called from Java using a Program Call Markup Language (PCML) source file that describes the parameters for the RPG program or procedure. The Java application can use PCML by constructing a ProgramCallDocument object with a reference to the PCML source file.

The ILE RPG compiler will generate PCML source for your ILE RPG program or module when you specify the PGMINFO(*PCML) compiler parameter along with the INFOSTMF compiler parameter to specify the name of an IFS output file to receive the generated PCML. For CRTBNDRPG, PCML is generated based on the contents of the *ENTRY PLIST or the Procedure Interface of the main procedure.

For CRTRPGMOD, PCML is also generated based on the Procedure Interfaces of any exported subprocedures (except Java native methods). When you use CRTRPGMOD, and create a service program, you specify the service program in your Java code using the setPath(String) method of the ProgramCallDocument class.

Calling RPG programs from Java using PCML

PCML Restrictions
The following are restrictions imposed by PCML regarding parameter and return value types.

  • The following data types are not supported by PCML:
    – Date
    – Time
    – Timestamp
    – Pointer
    – Procedure Pointer
    – 1-Byte Integer
    – 8-byte Unsigned Integer
  • Return values and parameters passed by value can only be 4 byte integers (10i 0).
  • Varying-length arrays, and data structures containing varying-length subfields are not supported.
  • When a data structure is used as a parameter for a *ENTRY PLIST, or a prototyped parameter is defined with LIKEDS, some PCML restrictions apply:
    – The data structure may not have any overlapping subfields.
    – The subfields must be coded in order; that is, the start position of each subfield must follow the end position of the previous subfield.
    – If there are gaps between the subfields, the generated PCML for the structure will have subfields named ″_unnamed_1″, ″_unnamed_2″ etc, of type ″char″.
  • RPG does not have the concept of output-only parameters. Any parameters that do not have CONST or VALUE coded have a usage of ″inputoutput″. For inputoutput parameters, the ProgramCallDocument class requires the input values for the parameter to be set before the program can be called. If the parameter is truly an output parameter, you should edit the PCML to change ″inputoutput″ to ″output″.

The compile will fail if you generate PCML for a program or module that violates one of the restrictions. The PCML will be generated, but it will contain error messages as comments. For example, if you use a Date field as a parameter, the PCML for that parameter might look like this:

PCML Restrictions


All rights reserved © 2018 Wisdom IT Services India Pvt. Ltd DMCA.com Protection Status

IBM - RPG Topics