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

Copyright IBM Corp, 2008, 2010. 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 should list the Java exception in the help for the message.

If the Java exception is one of these messages, please 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. Please 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 will prevent those objects from being freed during garbage collection. Eventually, it will be unable to create any more objects.

Ensure you are freeing objects if 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?

The Java version is set by the java.version system property. Please see the section on setting Java properties.

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

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; prior to 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 prior to V5R3
On releases prior to 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?

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 classpath 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; please see the Java documentation in the Info Center 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.

Note:Prior to 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 prior to V5R3, you will have to use the QIBM_RPG_JAVA_PROPERTIES environment variable to set this particular property.

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 as long as they are all the same, and as long as they are not used anywhere else in the actual properties.)

Examples:

 Set the java.version 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 java.version 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, please 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 classpath are being used in the JVM?

You can use the DMPJVM command to get information on the version and classpath 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 runtime

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

Please 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, please 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. Please 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 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. Please 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:

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

Top
End of document