Copyright IBM Corp, 2008, 2010. Change history
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:
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.
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.
The Java version is set by the java.version system property. Please see the section on setting Java properties.
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 classpath 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. |
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.
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.
class DebugStudent { static byte[] debugGetTeacher (Student s, byte course[]) { try { return s.getTeacher (course); } catch (Exception e) { e.printStackTrace(); return "".getBytes(); } } }
===> /qsys.lib/mylib.lib/mypgm.pgmWarning: 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.
===> ADDENVVAR QIBM_RPG_JAVA_PROPERTIES VALUE('-Dos400.stdout=file:/mydir/out.txt;-Dos400.stderr=file:/mydir.err.txt;')
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.
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.
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.
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.
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.
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.
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.
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:
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.
* 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
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:
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;
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;
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.