You are on page 1of 8

Additional RPG Coding for Using Java

When you are using ILE RPG with Java(TM), 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. See Figure 91 below for a complete
working example.

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,
see the JNI documentation at http://java.sun.com.
D JNI_GROUP_ADDED...
D
c
0
D JNI_GROUP_NOT_ADDED...
D
c
-1
D JNI_GROUP_ENDED...
D
c
0
*---------------------------------------------------------------* beginObjGroup - start a new group of objects that can all
*
be deleted together later
*---------------------------------------------------------------P beginObjGroup
b
export
D beginObjGroup
pi
10i 0
D
env
*
const
D
capacityParm
10i 0 value options(*nopass)
D rc
s
10i 0
D capacity
s
10i 0 inz(100)
/free
JNIENV_p = env;
if (%parms >= 2);
capacity = capacityParm;
endif;
rc = PushLocalFrame (JNIENV_p : capacity);
if (rc <> 0);
return JNI_GROUP_NOT_ADDED;
endif;
return JNI_GROUP_ADDED;
/end-free
P beginObjGroup
e
*---------------------------------------------------------------* endObjGroup - end the group of objects that was started
*
most recently
*---------------------------------------------------------------P endObjGroup
b
export
D endObjGroup
pi
10i 0
D
env
*
const
D
refObject
P
o
class(*java:'java.lang.Object')
D
const
D
options(*nopass)
D
newObject

P
D
D retVal
D refObject
D newObject
/free

/end-free
P endObjGroup

o
s
s
s

class(*java:'java.lang.Object')
options(*nopass)
o
class(*java:'java.lang.Object')
like(refObjectP) inz(*null)
like(newObjectP)

JNIENV_p = env;
if %parms() >= 2;
refObject = refObjectP;
endif;
newObject = PopLocalFrame (JNIENV_p : refObject);
if %parms() >= 3;
newObjectP = newObject;
endif;
return JNI_GROUP_ENDED;
e

Note:
You need the JNI environment pointer (described in Obtaining the JNI environment pointer below) 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
(called by java) when the native method returns, but otherwise it will never happen 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);

Figure 87 contains the sample source code for freeLocalRef.


Figure 87. Source Code for freeLocalRef
/*------------------------------------------------------*/
/* freeLocalRef
*/
/*------------------------------------------------------*/
P freeLocalRef...
P
B
EXPORT
D freeLocalRef...
D
PI
D
env
*
VALUE
D
localRef
O
CLASS(*JAVA
D
: 'java.lang.Object')
D
VALUE
/free
jniEnv_P = env;
DeleteLocalRef (env : localRef);
/end-free
P freeLocalRef...
P
E

Note:
You need the JNI environment pointer (described in Obtaining the JNI environment pointer below) 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);

Figure 88 contains the sample source code for getNewGlobalRef.

Figure 88. Source Code for getNewGlobalRef


/*------------------------------------------------------*/
/* getNewGlobalRef
*/
/*------------------------------------------------------*/
P getNewGlobalRef...
P
B
EXPORT
D getNewGlobalRef...
D
PI
O
CLASS(*JAVA
D
: 'java.lang.Object')
D
env
*
VALUE
D
localRef
O
CLASS(*JAVA
D
: 'java.lang.Object')
D
VALUE
/free
jniEnv_P = env;
return NewGlobalRef (env : localRef);
/end-free
P getNewGlobalRef...
P
E

Note:
You need the JNI environment pointer (described in Obtaining the JNI environment pointer below) to
call this wrapper.
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);

Figure 89 contains sample source code for freeGlobalRef.


Figure 89. Source Code for freeGlobalRef
/*------------------------------------------------------*/
/* freeGlobalRef
*/
/*------------------------------------------------------*/
P freeGlobalRef...
P
B
EXPORT
D freeGlobalRef...
D
PI
D
env
*
VALUE
D
globalRef
O
CLASS(*JAVA
D
: 'java.lang.Object')
D
VALUE
/free
jniEnv_P = env;
DeleteGlobalRef (env : globalRef);
/end-free
P freeGlobalRef...
P
E

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, you can see an example of
this coding in the last part of Figure 90.
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();

Figure 90 contains sample source code for getJniEnv.


Figure 90. Source Code for getJniEnv
*---------------------------------------------------------------* getJniEnv - attach-to or start the JVM
*
* Parameters:
* 1. inputOpts - string of options separated by whatever the
*
last character in the string is.
*
For example
*
-Djava.pool.size=800;-Dcompile=none;
*
- ignored if the JVM is already started
*
- classpath is taken from the CLASSPATH environment
*
variable, but it could be passed to this procedure
*
using the -Djava.class.path option.
*
Sample calls:
*
env = getJniEnv()
// take the defaults
*
env = getJniEnv('-Djava.poolsize=800;'
*
+ '-Dcompile=none;')
// specify 2 options
*
env = getJniEnv('-Djava.poolsize=800!'
*
+ '-Dcompile=none!')
// use ! as separator
*
*---------------------------------------------------------------P getJniEnv
b
export
D getJniEnv
pi
*
D
inputOpts
65535a
varying const options(*nopass)
D env
s
*
inz(*null)
/free
env = attachJvm();
if (env = *null);
if %parms() = 0
or %len(inputOpts) =
0;
env =
startJvm();
else;
env =
startJvm(inputOpts);
endif;
endif;
return env;
/end-free
P getJniEnv
e
*---------------------------------------------------------------* startJvm - try to start the JVM
*
* Parameters:
* 1. inputOpts - string of options separated by whatever the
*
first character in the string is
*
- ignored if the JVM is already started
*---------------------------------------------------------------P startJvm
b
export
D startJvm
pi
*
D
inputOptsP
65535a
varying const options(*nopass)
D initArgs
ds
likeds(JavaVMInitArgs)
D options
ds
likeds(JavaVMOption) occurs(10)
D jvm
s
like(JavaVM_p)
D env
s
like(JNIENV_p) inz(*null)
D rc
s
10i 0
D len
s
10i 0
D prefix
s
100a
varying
D pOptions
s
*
inz(%addr(options))
D i
s
10i 0
D classpath
S
65535a
varying
* For handling the input options
D splitChar
s
1a
D pos
s
10i 0
D nextPos
s
10i 0
D inputOpts
s
65535a
varying
D inputOptsPtr
s
*
D freeThisOccur
s
n
dim(%elem(options)) inz(*off)
/free
monitor;
initArgs = *allx'00';
JNI_GetDefaultJavaVMInitArgs (%addr(initArgs));
initArgs.version = JNI_VERSION_1_2;
// add the classpath option, if necessary

classpath = getClasspath();
if (%len(classpath) > 0);
initArgs.nOptions = initArgs.nOptions + 1;
%occur(options) = initArgs.nOptions;
freeThisOccur(initArgs.nOptions) = *on;
options = *allx'00';
prefix = '-Djava.class.path=:';
len = %len(prefix) + %len(classpath) + 1;
options.optionString = %alloc (len);
%str(options.optionString : len) =
cvtToAscii(prefix)
+
cvtToAscii(classpath);
endif;
// add any other options that were passed in
if

%parms > 0
and %len(inputOptsP) > 0;
inputOpts = cvtToAscii(inputOptsP);
inputOptsPtr = %addr(inputOpts) + 2;
splitChar = %subst(inputOpts : %len(inputOpts) : 1);
pos = 1;
dow pos <= %len(inputOpts);
nextPos = %scan(splitChar : inputOpts : pos);
len = nextPos - pos;
%subst(inputOpts : nextPos : 1) = x'00';
initArgs.nOptions = initArgs.nOptions + 1;
%occur(options) = initArgs.nOptions;
options = *allx'00';
options.optionString = inputOptsPtr + pos - 1;
enddo;

pos = nextPos + 1;

endif;
if initArgs.nOptions > 0;
initArgs.options = pOptions;
endif;
rc = JNI_CreateJavaVM (jvm : env : %addr(initArgs));
if (rc <> 0);
env = *null;
endif;
rc = JNI_CreateJavaVM (jvm : env : %addr(initArgs));
if (rc <> 0);
env = *null;
endif;
on-error;

env = *null;

endmon;
// free any storage allocated for the options
for i = 1 to initArgs.nOptions;
if (freeThisOccur(i));
%occur(options) = i;
dealloc(n) options.optionString;
endif;
endfor;
return env;
/end-free
P startJvm
e
*---------------------------------------------------------------* attachJvm - try to attach to the JVM
*---------------------------------------------------------------P attachJvm
b
export
D attachJvm
pi
*
D attachArgs
ds
likeds(JavaVMAttachArgs)
D jvm
s
like(JavaVM_p) dim(1)
D nVms
s
like(jsize)
D env
s
*
inz(*null)
D rc
s
10i 0
/free
monitor;

rc = JNI_GetCreatedJavaVMs(jvm : 1 : nVms);
if (rc <> 0);
return *null;
endif;

if (nVms = 0);
return *null;
endif;
JavaVM_P = jvm(1);
attachArgs = *allx'00';
attachArgs.version = JNI_VERSION_1_2;
rc = AttachCurrentThread (jvm(1) : env : %addr(attachArgs));
if (rc <> 0);
env = *null;
endif;
on-error;

env = *null;

endmon;
return env;
/end-free
P attachJvm
e
*---------------------------------------------------------------* getClasspath - retreive the CLASSPATH environment variable
*---------------------------------------------------------------P getClasspath
B
export
D getClasspath
PI
65535A
varying
D Qp0zGetEnvNoInit...
D
PR
*
extproc('Qp0zGetEnvNoInit')
D
name
*
value options(*string)
D envVarP
S
*
/free
envvarP = Qp0zGetEnvNoInit('CLASSPATH');
if (envvarP = *NULL);
return '';
else;
return %str(envvarP : 65535);
endif;
/end-free
P getClasspath
E
*---------------------------------------------------------------* cvtToAscii - convert from EBCDIC to ASCII
*---------------------------------------------------------------P cvtToAscii
B
export
D cvtToAscii
PI
65535A
varying
D
input
65535A
const varying
D QDCXLATE
PR
extpgm('QDCXLATE')
D
len
5P 0 const
D
cnvData
65535A
options(*varsize)
D
cnvTbl
10A
const
D
cnvLib
10A
const
D retval
S
like(input)
D retvalRef
S
1A
based(retvalPtr)
/free
retval = input;
retvalPtr = %addr(retval) + 2;
// set ptr after the length part
QDCXLATE(%len(retval): retvalRef : 'QASCII': 'QSYS');
return retval;
/end-free
P cvtToAscii

Figure 91. Using the wrappers for the JNI functions


Java class
class TestClass{
String name = "name not set";
TestClass (byte name[]) {
this.name = new String(name);
}
void setName (byte name[]) {
this.name = new String(name);
}

String getName () {
return this.name;
}

RPG program
H THREAD(*SERIALIZE)
H BNDDIR('JAVAUTIL')
// (JAVAUTIL is assumed to the binding directory that lists
// the service program containing the procedures described
// below)
/copy JAVAUTIL
// (JAVAUTIL is assumed to be the source member containing the

// prototypes for the procedures described below)


D TestClass
C
'TestClass'
D StringClass
C
'java.lang.String'
D newTest
PR
O
EXTPROC(*JAVA : TestClass
D
: *CONSTRUCTOR)
D
name
25A
VARYING CONST
D getName

PR

D
D

CLASS(*JAVA : StringClass)
extproc(*JAVA : TestClass
: 'getName')

D setName
D
D
newName

PR

D newString
D
D
value

PR

D nameValue
D
D

PR

25A
O
65535A
25A

extproc(*JAVA : TestClass
: 'setName')
VARYING CONST
EXTPROC(*JAVA : StringClass
: *CONSTRUCTOR)
VARYING CONST
VARYING
extproc(*JAVA : StringClass
: 'getBytes')

D myTestObj
S
LIKE(newTest)
D myString
S
LIKE(newString)
D env
S
LIKE(getJniEnv)
/free
// Let the RPG runtime start the JVM, by calling a
// Java method before calling any JNI functions
myString = newString ('');
// Get the JNI environment pointer so that JNI functions
// can be called. The "myString" object should be freed now
env = getJniEnv();
freeLocalRef (env : myString);
// Set the beginning marker for an "object group"
// so that any objects created between now and the
// "end object group" can be freed all at once.
beginObjGroup (env);
// Create a Test object to work with
// We do not want this object to be freed with the
// other objects in the object group, so we make it
// a permanent object
myTestObj = newTest ('RPG Dept');
myTestObj = getNewGlobalRef (env : myTestObj);
// Get the current "name" from the Test object
// This creates a local reference to the Name object
myString = getName (myTestObj);
dsply (nameValue(myString));
// Change the name
setName (myTestObj : 'RPG Department');
// Get the current "name" again. This will cause
// access to the previous local reference to the old name
// to be lost, making it impossible for this RPG
// program to explicitly free the object. If the object
// is never freed by this RPG program, Java could never
// do garbage-collection on it, even though the old String
// object is not needed any more. However, endObjGroup
// will free the old reference, allowing garbage collection
myString = getName (myTestObj);
dsply (nameValue(myString));
// End the object group. This will free all local
// references created since the previous beginObjGroup call.
// This includes the two references created by the calls
// to getName.
endObjGroup (env);
// Since the original Test object was made global, it can
// still be used.
setName (myTestObj : 'RPG Compiler Dept');
// The original Test object must be freed explicitly
// Note: An alternative way to handle this situation
//
would be to use nested object groups, removing
//
the need to create a global reference
//
beginObjGroup ------------.
//
create myTestObj
|
//
beginObjGroup ---------. |
//
...
| |
//
endObjGroup
---------' |
//
use myTestObj again
|

//
endObjGroup
------------'
freeGlobalRef (env : myTestObj);
return;
/end-free

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.
(C) Copyright IBM Corporation 1992, 2006. All Rights Reserved.