IBM Support

Troubleshoot calls from RPG to Java

General Page

Troubleshooting calls from RPG to Java using EXTPROC(*JAVA)

Copyright IBM Corp, 2008, 2013. Change history

Common problems:

Frequently asked questions:


The call to a Java method is failing with RNX0301

The Java method failed with a Java exception

To see the Java exception causing the error, inspect the RNX0301 message that you received. It lists the Java exception in the help for the message.

If the Java exception is one of these messages, check that section of this document first: NoClassDefFound, or NoSuchMethodError.

If you want to see exactly which Java class threw the exception, see the section on getting an exception trace from Java.

If you think the exception should not be occurring, check whether one of the following problems could be causing the error:

My Java class works fine when called from Java, but doesn't work when called from RPG.

The most common cause of this problem is that the JVM is running under a different Java version when you call the Java method from your RPG program. See the section on setting the Java version.

The application runs fine and then suddenly starts failing.

When you create Java objects from your RPG programs, or when you call Java methods that return Java objects, the JVM keeps track of the fact that you have references to those objects. Until you tell the JVM that you don't need those objects any more, it those objects are prevented from being freed during garbage collection. Eventually, Java will be unable to create any more objects.

Ensure you are freeing objects when you no longer need them. See the section on freeing objects.

There may be a Java system property that controls the number of objects that can be created. Consult the list of the available Java system properties in the Java section of the Information Center.

How can I set the Java version?

If you are using the Classic JVM, available before 7.1, the Java version is set by the system property. See the section on setting Java properties.

If you are using "IBM Technology for Java", the Java version is set using the JAVA_HOME environment variable: https://www.ibm.com/docs/en/ssw_ibm_i_74/rzaha/runfirst.htm.

  • In 6.1, setting the property will cause Java to use the Classic JVM. If you do not set the property, Java will use the "IBM Technology for Java" JVM.
  • In 7.1, the Classic JVM is not available, so the property is ignored.

Why does Java seem to ignore my CLASSPATH or QIBM_RPG_JAVA_PROPERTIES environment variables?

  • The environment variables are only used when the RPG runtime is starting the JVM. If the JVM has already been started, the environment variables would be ignored. The JVM may have already been started; possibly by some other RPG program making a Java method call, or by some explicit JNI coding to start the JVM.
    Tip: If you have several different RPG applications that all need to call Java methods, set up LEVEL(*SYS) environment variables for CLASSPATH and QIBM_RPG_JAVA_PROPERTIES; the class path should include all the classes that might be need by any application. With a LEVEL(*SYS) environment variable, every job starts with that environment variable already set.
  • The environment variables are case-sensitive. For example, an environment variable named "class path" or "qibm_rpg_java_properties" would not be found.
  • If the properties are not specified correctly, they will be ignored. See the section on Java properties.

How do I free my Java objects?

See the Java section in the ILE RPG Programmer's Guide information on how to free Java objects. You can free them one at a time, or free several objects at once.

If you are already freeing your objects, you may need to free them more often. For example, if you free all your objects at the end of your program, but within your program you create several objects for each record of a file, then you should free objects after the processing for each record, rather than waiting until the end of the program.

How can I get an exception trace from Java?

If you are V5R3 or later
See the ILE RPG Programmer's Guide on how to use the QIBM_RPG_EXCP_TRACE environment variable. (This is available starting in V5R3; before V5R3 it is not possible to get an exception trace when calling Java methods using the EXTPROC(*JAVA) support.)

To be able to use this support, you must also recompile your RPG program on a V5R3 or greater system, and the target release cannot be for a release earlier than V5R3.

Unlike other environment variables associated with Java, the QIBM_RPG_EXCP_TRACE environment variable may be set and unset at any time. It is checked whenever a Java method call ends with an exception.

If you are on a release before V5R3
On releases before V5R3, it is more complex to obtain a Java exception trace. You must write a small Java class with a wrapper method to make the Java method call that is failing, then temporarily change your RPG program to call that wrapper method. If it is a static method, define the wrapper method the same as the original method. If it is an instance method, define the wrapper method as a static method with an extra first parameter that is an object of the instance class; see the example below, which adds a Student parameter to the static method. For example, if your RPG program is calling method
int getTeacher(byte course[])
in class Student, then you would write the following Java class, and change a debug version of your RPG program to call method
byte[] debugGetTeacher(Student s, byte course[]) in class DebugStudent. The wrapper method will handle printing the exception trace.
      class DebugStudent {
         static byte[] debugGetTeacher (Student s, byte course[]) {
            try {
               return s.getTeacher (course);
            }
            catch (Exception e) {
               e.printStackTrace();
               return "".getBytes();
            }
         }
      }
   

How can I see the System.out and System.err output?

  • If you run your program in batch, you will get a QPRINT file containing the Java output.
  • If you run your program from QSH, you will get the output to the QSH screen. To run your program from QSH, call it like this:
      ===> /qsys.lib/mylib.lib/mypgm.pgm
    
    Warning: If you call your program this way, be aware that the parameters will be passed as null-terminated strings. In general, this way is only convenient if you have no parameters, since you would have to have different processing in your program for your parameters, depending on whether you were called from the command line or from QSH.
  • Otherwise, you must do a few things to get the output. Note that the environment variables must be set before the JVM is started, and the file descriptors must be opened before any Java output is done:
    1. Set the Java property os400.stdout=file:path and os400.stderr=filepath, specifying the locations where you want the output to go. For example, if you use QIBM_RPG_JAVA_PROPERTIES to set the Java properties, and you want the output to go to files /mydir/out.txt and /mydir/err.txt, you would specify
           ===> ADDENVVAR QIBM_RPG_JAVA_PROPERTIES
                   VALUE('-Dos400.stdout=file:/mydir/out.txt;-Dos400.stderr=file:/mydir.err.txt;')
        
    2. Set the environment variable QIBM_USE_DESCRIPTOR_STDIO to the value 'Y'.
    3. Check that the three standard-I/O file descriptors are open. Create the CHECKSTDIO program (source code below) and call it before you call your RPG application, or you could call it from your RPG application.

How do I set the class path?

The class path can be set by the CLASSPATH environment variable, or by the java.class.path property. The class path consists of directories and JAR files. The entries are separated by a colon.

For example, if you have .class objects in directory /mydir/myclasses and you also have JAR files /sysclasses/app1.jar and /myclasses/myjar.jar, then your class path would look like this:

   /myclasses:/sysclasses/app1.jar:/myclassses/myjar.jar

If you have a directory containing JAR files, it is not sufficient to just specify the directory. You must explicitly specify each JAR file.

   /myjars/jar1.jar:/myjars/jar2.jar:/myjars/jar3.jar

Also, see the section Why does Java seem to ignore my CLASSPATH or QIBM_RPG_JAVA_PROPERTIES environment variables.

How do I set Java properties?

You can set Java properties using the SystemDefault.properties file. There are several different places you can store this file; see the Java documentation in IBM documentation for more information.

You can set Java properties using the QIBM_RPG_JAVA_PROPERTIES environment variable.

If you use both the SystemDefault.properties file and the QIBM_RPG_JAVA_PROPERTIES environment variable, the properties specified by the environment variable will override the properties specified by the file.

Note: The Java properties are case-sensitive. For example, setting the "java.version" property will have no effect; you must set the "java.version" property.

Notes:

  • Before V5R3, any setting for the java.version property in the SystemDefault.properties file was ignored when the JVM was started by the JNI (Java Native Interface). If you are on a release before V5R3, you will have to use the QIBM_RPG_JAVA_PROPERTIES environment variable to set this particular property.
  • In 6.1, setting the java.version property will cause Java to use the Classic JVM. If you do not set the java.version property, Java will use the "IBM Technology for Java" JVM.
  • Starting in 7.1, the java.version property is ignored. Only the "IBM Technology for Java" JVM is available.

How do I use the QIBM_RPG_JAVA_PROPERTIES environment variable?

The environment variable must be set before the JVM is created. The JVM will be created when the first RPG program in the job calls a Java method. Any further changes to the environment variable will be ignored in that job.

The properties are specified the same as for the QSH java command, in the form "-Dproperty=value".

The properties are case-sensitive; "java.version" is not the same as "JAVA.VERSION".

The value of the environment variable is in the form "property;property;property;" where each property is terminated by a semicolon. (It is not strictly necessary to use a semicolon; other characters could be used if they are all the same, and if they are not used anywhere else in the actual properties.)

Examples:

 Set the  property to version 1.4
    ADDENVVAR QIBM_RPG_JAVA_PROPERTIES VALUE('-Djava.version=1.4;')
 Set the os400.run.verbose property to true
    ADDENVVAR QIBM_RPG_JAVA_PROPERTIES VALUE('-Dos400.run.verbose=true;')
 Set the  and os400.stdout properties
    ADDENVVAR QIBM_RPG_JAVA_PROPERTIES VALUE('-Djava.version=1.4;os400.stdout=file:myfile.txt;')
 Same as the previous example, but use a different terminator character, '!'
    ADDENVVAR QIBM_RPG_JAVA_PROPERTIES VALUE('-Djava.version=1.4!os400.stdout=file:myfile.txt!')

For more information, see the ILE RPG Programmer's Guide. The information is in all versions of the manual starting with the V5R3 manual.

How can I find out what Java version and class path are being used in the JVM?

On 7.1, or if you are not using a Classic JVM

Use the WRKJVMJOB command.

Before 7.1, if you are using a Classic JVM

You can use the DMPJVM command to get information on the version and class path and many other things. The JVM must have already been started before DMPJVM is done.

Also, the DMPJVM command must be issued from a different job. An easy way to do that is to submit the DMPJVM command for your current job. For example, if your current job is QPADEV0002 JSMITH 023816, then you could do the following command, and then inspect the spooled output.

   ===> DMPJVM JOB(023816/JSMITH/QPADEV0002)

You can also retrieve any system property by calling the getProperty method in the System class. See the source below, for a program that interactively displays properties.

Determine exactly which Java classes are being used at run time

Specify the os400.run.verbose=true Java property.

See the section on setting Java properties for more information.

If the output flashes on the screen too fast for you to see it, or does not appear at all, see the section on seeing the System.out and System.err output.

Changing Java classes after the JVM has been started

If the JVM has already been created in your job, and you then change a Java class, or create a new Java class, the changes will not be recognized by Java in that job. You must sign off and sign back on again to have Java "see" your changes.

While developing Java classes for use by RPG, it is sometimes more convenient to submit your tests to batch rather than continually sign off and back on. If you use SBMJOB for your tests, be sure to ask for your current environment variables to be used for the submitted job:

===> SBMJOB CPYENVVAR(*YES)

Note: It is not necessary to start a new job if you only change your RPG programs.

The Java exception is NoClassDefFound

If the class mentioned in the Java exception is not the same as the class of the method being called, it is likely that the called Java method received this exception. See the section on failed Java method calls.

The call could not complete because Java could not find the class specified by the RPG prototype for the Java call. Check the following. If you find any problems, correct them and try again.

  • The EXTPROC for the Java method has the Java class spelled correctly, and with the correct case ("MYCLASS" and "myclass" are not considered the same by Java).
  • The class is in a JAR file, but the JAR file is not mentioned in the class path. See the section on setting the class path.
  • The class is new on the system, and the JVM was previously started in the job (probably by other calls from RPG to Java). See the section on changing Java classes after the JVM has been started.

The Java exception is NoSuchMethodError

Note: If the class mentioned in the Java exception is not the same as the class of the method being called, it is likely that the called Java method received this exception. See the section on failed Java method calls.

If the class mentioned in the Java exception is the same as the class of the method being called, then the call could not complete because Java could not find the method specified by the RPG prototype for the Java call. Check the following:

  • The EXTPROC for the Java method has the Java class and method name spelled correctly, and with the correct case ("MYCLASS" and "myclass" are not considered the same by Java).
  • The Java method has the static keyword, but the STATIC keyword was not specified for the RPG prototype.
  • The Java method has no return type, and the name of the method is the same as the name of the class. If this is the case, then the Java method is a constructor, and the special method "name" of *CONSTRUCTOR must be used for the RPG prototype. Read the section in the ILE RPG Programmer's Guide on how to code the RPG prototype for a Java constructor.
  • The signature does not match the actual Java class. If this is the case, read the section in the ILE RPG Programmer's Guide on how to code the RPG prototype for a Java method. To see the expected signature for the Java method, use the javap command; there is an example in the help for the RNX0301 message.
  • The method is new in the class, or the signature (return type and parameters) have changed, and the JVM was previously started in the job (probably by other calls from RPG to Java). See the section on changing Java classes after the JVM has been started.
  • The Java method requires a particular Java version. If this is the case, see the sections on finding out what Java version is being used in the JVM and setting the Java version.

Source code for program CHECKSTDIO

This program can be used as part of the setup necessary to see the Java output from an interactive job. See the section on How can I see the System.out and System.err output? for the other setup that is required.

This program should be called when the job starts, to ensure that the three "Standard I/O" descriptors 0, 1, and 2 are opened.

Program CHECKSTDIO

      * Create the program with a simple CRTBNDRPG
      * Call it with no parameters at the beginning of the job

     h bnddir('QC2LE') dftactgrp(*no)
     Fqsysprt   o    f   80        printer
     D prtds           ds            80

     D O_CREAT         C                   x'00000008'
     D O_TRUNC         C                   x'00000040'
     D O_RDONLY        C                   x'00000001'
     D O_WRONLY        C                   x'00000002'
     D O_RDWR          C                   x'00000004'

     D O_ACCMODE       c                   %BITOR(O_RDONLY
     D                                          : %BITOR(O_WRONLY
     D                                                 : O_RDWR))

     D S_IRUSR         C                   x'0100'
     D S_IROTH         C                   x'0004'
     D S_IWUSR         C                   x'0080'
     D S_IWOTH         C                   x'0002'

     D chk             pr              n
     D   descriptor                  10i 0 value
     D   mode                        10i 0 value
     D   aut                         10i 0 value
     D   other_valid_mode...
     D                               10i 0 value

     D sndErr          pr
     D   msg                       1000a   const varying

     D ok              s               n

      /free
       // Validate or open descriptors 0, 1 and 2
       ok = chk (0
               : 0 + O_CREAT + O_TRUNC + O_RDWR
               : 0 + S_IRUSR + S_IROTH
               : 0 + O_RDONLY)
       and  chk (1
               : 0 + O_CREAT + O_TRUNC + O_WRONLY
               : 0 + S_IWUSR + S_IWOTH
               : 0 + O_RDWR)
       and  chk (2
               : 0 + O_CREAT + O_TRUNC + O_WRONLY
               : 0 + S_IWUSR + S_IWOTH
               : 0 + O_RDWR);

       // If the descriptors were not all correct,
       // signal an exception to our caller
       if not ok;
          sndErr ('Descriptors 0, 1 and 2 not opened successfully.');
       endif;
       *inlr = '1';

       begsr *pssr;
          // endsr *cancl will signal an exception to the caller
       endsr '*CANCL';
      /end-free
     P chk             b
     D chk             pi              n
     D   descriptor                  10i 0 value
     D   mode                        10i 0 value
     D   aut                         10i 0 value
     D   other_valid_mode...
     D                               10i 0 value

     D open            pr            10i 0 extproc('open')
     D   filename                      *   value options(*string)
     D   mode                        10i 0 value
     D   aut                         10i 0 value
     D   unused                      10i 0 value options(*nopass)

     D closeFile       pr            10i 0 extproc('close')
     D   handle                      10i 0 value

     D fcntl           pr            10I 0 extproc('fcntl')
     D   descriptor                  10I 0 value
     D   action                      10I 0 value
     D   arg                         10I 0 value options(*nopass)

     D F_GETFL         c                   x'06'

     D flags           s             10i 0
     D new_desc        s             10i 0

     D actual_acc      s             10i 0
     D required_acc    s             10i 0
     D allowed_acc     s             10i 0
      /free
          flags = fcntl (descriptor : F_GETFL);
          if flags < 0;
             // no flags returned, attempt to open this descriptor
             new_desc = open ('/dev/null' : mode : aut);
             if new_desc <> descriptor;
                // we didn't get the right descriptor number, so
                // close the one we got and return '0'
                if new_desc >= 0;
                   closeFile (new_desc);
                   prtds = 'wanted desc = ' + %char(descriptor) + ' '
                         + 'got desc = ' + %char(new_desc);
                   write qsysprt prtds;
                endif;
                return '0';
             endif;
          else;
             // check if the file was opened with the correct
             // access mode
             actual_acc = %bitand (flags : O_ACCMODE);
             required_acc = %bitand (mode : O_ACCMODE);
             allowed_acc = %bitand (other_valid_mode : O_ACCMODE);
             if  actual_acc <> required_acc
             and actual_acc <> allowed_acc;
                prtds = 'desc = ' + %char(descriptor) + ' '
                      + 'act = ' + %char(actual_acc) + ' '
                      + 'oth = ' + %char(allowed_acc);
                write qsysprt prtds;
                return '0';
             endif;
          endif;

          prtds = 'desc = ' + %char(descriptor) + ' '
                + 'ok';
                write qsysprt prtds;
          return '1';
      /end-free
     P chk             e
     P sndErr          b
     D sndErr          pi
     D   msg                       1000a   const varying
     D qmhsndpm        pr                  extpgm('QMHSNDPM')
     D   msgid                        7a   const
     D   msgfile                     20a   const
     D   msgdata                    500a   const
     D   datalen                     10i 0 const
     D   msgtype                     10a   const
     D   stackentry                  10a   const
     D   stackoffset                 10i 0 const
     D   msgkey                       4a   const
     D   errorCode                         likeds(errCode)
     D key             s              4a
     D errCode         ds
     D   bytesProv                   10i 0 inz(0)
     D   bytesAvail                  10i 0
      /free
         qmhsndpm ('CPF9897'
                 : 'QCPFMSG   *LIBL'
                 : msg
                 : %len(msg)
                 : '*ESCAPE' : '*' : 3  // send to caller of this program
                 : key : errCode);
      /end-free
     P sndErr          e

Source code for program JAVAPROP

This program can be used for casual inspection of the Java properties in effect. Use the DMPJVM command if you want full information about the JVM.

Warnings:

  1. Running this program will start the JVM if it has not already started. If you have some special code that runs before the JVM is started (for example, to set up environment variables) be sure to do that first.
  2. This program assumes that you have a binding directory called JNIUTIL containing the procedures getJniEnv, beginObjGroup and endObjGroup, and that the prototypes for these procedures are in a source file *LIBL/QRPGLESRC(JNIUTIL). You can find the source code for these procedures in the ILE RPG Programmer's Guide.

    If you aren't sure what the property names are, you could use the ALLJVAPROP program. See its source below.

H dftactgrp(*no)
H thread(*serialize)
H bnddir('JNIUTIL')
 /copy jniutil_p
D SystemClass     c                   'java.lang.System'
D StringClass     c                   'java.lang.String'
D getProperty     pr              o   extproc(*java
D                                           : SystemClass
D                                           : 'getProperty')
D                                     class(*java : StringClass)
D                                     static
D   prop                          o   const class(*java : StringClass)
D getBytes        pr         65535a   varying
D                                     extproc(*java
D                                           : StringClass
D                                           : 'getBytes')
D newString       pr              o   extproc(*java
D                                           : StringClass
D                                           : *constructor)
D   val                      65535a   const varying

D str             s               o   class(*java : stringClass)
D result          s          65535a   varying
D dsp             s             52
D prop            s             40a   varying inz('java.version')
D p               s             10i 0
D helpmsg         s             52a
D env             s               *

 /free
   helpmsg = 'Enter "q" for the property, when you want to quit';
   dsply helpmsg;
   if env = *NULL;
      env = getJniEnv();
   endif;
   dow prop <> 'q';
       prop = ' ';
       dsply 'Property?' ' ' prop;
       if prop = 'q' or prop = 'Q';
          *inlr = '1';
          return;
       elseif prop = ' ' or prop = '?';
          dsply helpmsg;
          iter;
       endif;

       // use beginObjGroup to mark the beginning of the
       // code that uses Java objects, and use endObjGroup
       // to free all those objects
       beginObjGroup (env : 100);
          str = newString (prop);
          str = getProperty (str);
          if str = *null;
             result = 'That property was not found';
          else;
             result = getBytes (str);
          endif;
       endObjGroup (env);

       // loop 52 bytes at a time to display the property
       p = 1;
       dow p <= %len(result);
          dsp = %subst(result : p); // get 52 or fewer bytes
          dsply dsp;
          p = p + 52;
       enddo;
   enddo;
   return;

Source code for program ALLJVAPROP

This program can be used to see all the possible Java properties you could set. Note that the warnings listed for the JAVAPROP program also apply to this program.

H dftactgrp(*no)
H thread(*serialize)
H bnddir('JNIUTIL')
 /copy jniutil_p
D SystemClass     c                   'java.lang.System'
D StringClass     c                   'java.lang.String'
D PropertiesClass...
D                 c                   'java.util.Properties'
D EnumerationClass...
D                 c                   'java.util.Enumeration'
D ObjectClass     c                   'java.lang.Object'
 * String: byte[] getBytes();
D getBytes        pr         65535a   varying
D                                     extproc(*java
D                                           : StringClass
D                                           : 'getBytes')
 * System: Properties getProperties();
D getProperties   pr              o   class(*java: PropertiesClass)
D                                     extproc(*java
D                                           : SystemClass
D                                           : 'getProperties')
D                                     static
 * Properties: Enumeration propertyNames();
D propertyNames   pr              o   class(*java: EnumerationClass)
D                                     extproc(*java
D                                           : PropertiesClass
D                                           : 'propertyNames')
D
 * Enumeration: boolean hasMoreElements();
D hasMoreElements...
D                 pr              n   extproc(*java
D                                           : EnumerationClass
D                                           : 'hasMoreElements')
D
 * Enumeration: Object nextElement();
D nextElement...
D                 pr              o   class(*java:ObjectClass)
D                                     extproc(*java
D                                           : EnumerationClass
D                                           : 'nextElement')
D

D str             s               o   class(*java : StringClass)
D enumeration     s               o   class(*java : EnumerationClass)
D property        s             52a   varying
D env             s               *

 /free
   env = getJniEnv();
   beginObjGroup (env : 100);
      enumeration = propertyNames (getProperties());
      dow hasMoreElements (enumeration);
         str = nextElement (enumeration);
         property = getBytes (str);
         dsply property;
      enddo;
   endObjGroup (env);

   return;

Details of changes to this document

  • Version 3, 2013-03-26
    • Updated information about the java.version property for 6.1 and 7.1.
    • Updated information about how to get information about the JVM for J9.
  • Version 2, 2010-06-02
    • The recommended ALLOCSTDIO program was flawed in that it just opened three random descriptors, and it allowed them to be closed again.

      It has been replaced by the CHECKSTDIO program which first checks whether descriptors 0, 1, and 2 are already open, and it checks whether they are correctly opened for input (0) or output (1 and 2).

      The CHECKSTDIO program does not provide any way of closing the descriptors later.

End of document

Attachments

  1. CHECKSTDIO source: CHECKSTDIO.rpgle_.txt
  2. JAVAPROP source: JAVAPROP.rpgle_.txt
  3. ALLJVAPROP source: ALLJVAPROP.rpgle_.txt

[{"Business Unit":{"code":"BU058","label":"IBM Infrastructure w\/TPS"},"Product":{"code":"SS69QP","label":"Rational Development Studio for i"},"Component":"ILE RPG Compiler","Platform":[{"code":"PF012","label":"IBM i"}],"Version":"All Versions","Edition":"","Line of Business":{"code":"LOB57","label":"Power"}}]

Document Information

Modified date:
27 April 2021

UID

ibm11117695