Guild Companies, Inc.  
 
Midrange Programmer - How-To Advice & Free Code
OS/400 Edition
Volume 1, Number 4 - February 28, 2002

Calling RPG Native Methods from Java

by Kevin Vandever

[The code for this article is available for download.]

The last time I heard the term native used in connection with the AS/400 was way back when I was converting programs written in RPG II, running in the System/36 environment, to native RPG/400 programs. Well, native strikes again! This time in the form of RPG native methods, which are RPG- based subprocedures compiled into ILE service programs, yet defined and called from Java as native methods. "Hogwash!" you say? "Can't be done!" Well it can. And it has. And you can thank the Java Native Interface (JNI) for the pleasure.

WorksRight

So What Gives?

In my article "Prototyping and Calling Java Methods from RPG," I explained half the story; that is, accessing Java methods from RPG. I showed you the new O data type to signify a Java object, and illustrated how to use objects as input parameters for Java method calls, as well as return values from Java method calls. I also showed you the new parameters on the EXTPROC keyword that are used when prototyping Java methods. I then combined the techniques to convert RPG data types to Java, and back again, and to call Java methods from the comfort of your own RPG program. Now it's time for the second half of the story: Calling RPG native methods from Java. And everything you learned from the first article can be applied here.

An Old Friend

I have provided a simple source member called CUSTOMER (customer.txt) and a separate source member containing the prototype (customerpr.txt), used to validate a customer ID. First, let's take a look at the prototype (customerpr.txt). You may recognize the syntax from my previous article. I am prototyping a Java method called checkCust, which returns an indicator value, data type N, stating whether or not the customer ID is valid. I am passing the subprocedure an 8-byte alpha customer ID. The Java method prototype is accomplished by using the new enhancements to the keyword ExtProc. The three new parameters are *JAVA, to signify that you are prototyping a Java method; the fully qualified class that contains the method, ValCust; and the name of the method, checkCust. The name of the prototype does not have to match the name of the Java method, but if the Java method name is descriptive enough, I will name my prototype the same.

The RPG Native Method

Now let's take a look at the CUSTOMER source code (customer.txt). The checkCust subprocedure simply accepts the 8-byte customer ID, chains to the CstMst file, and returns the indicator, the %Found built-in function, stating whether or not the customer was found. As a side note, I have coded the C-specs using the new free-form RPG. You don't have to use this technique, but it is available with OS/400 V5R1 and is very powerful. (For more information on free-from C-specs, check out my article "Let Your Hair Down with Free- Formed C-Specs.")

There are three things required to make this a native method. First, it must be a subprocedure that is compiled into a service program. Second, you must use the keyword EXPORT on the first P-spec that begins the subprocedure definition. This will allow the subprocedure to be used outside of the CUSTOMER module. Last, you must make this module thread-safe. You do this by inserting an H-spec and specifying the Thread(*SERIALIZE) keyword/parameter combination. Java runs in a multi-threaded environment, within the same job, as opposed to multiple jobs, as most of us are used to. These threads can cause serious trouble if they call the same module simultaneously within the same job; therefore, you must somehow protect your module's static storage in some manner, and even though RPG is not yet a multi- thread-capable language, you can still make it thread-safe by serializing the calls. Basically, this means that if two Java threads call the same RPG module within the same job, Thread(*SERIALIZE) states that the first call to a module must completely finish before the second one is called. If the module were not made thread-safe in this manner, bad things would happen to static storage. So this module contains all the requirements necessary for a native method. Now it's time to compile. Remember to create a physical file called CSTMST that contains an 8-byte customer ID so that these examples will compile cleanly.

First, you create an RPG module, using option 15 from PDM or the following command:

CRTRPGMOD MODULE(Your_Lib/CUSTOMER) SRCFILE(Your_Source_File/QRPGLESRC)

Then you must create a service program from the module you just created. You can accomplish this with the following command:

CRTSRVPGM SRVPGM(Your_Lib/CUSTOMER) EXPORT(*ALL)

The interesting thing is that, now that you've done all this RPG coding, the native method will actually be defined and called from a Java class, not from another ILE object. Let's see how it's done.

The Java App

There are three areas of the Java source code (valcust.java) that I want to cover: the defining of the native method, the JNI load of the ILE service program, and the actual method call to the native method.

Let's start with the native method definition. The following line of code in the Java class is used to define the native method as part of the ValCust class:

public native boolean checkCust (byte custID[]);

Let's break the line down a little, shall we? The method name is checkCust, and it accepts a byte array as its parameter. Think of a byte array as an alpha field in RPG-speak; in fact, if you look back at the prototype for this native method, you'll see it is defined to accept an 8-byte alpha field. Directly to the left of the method name is the data type for the return value. In my example, I want the checkCust method to return a Boolean value, to tell me whether or not the customer ID I passed is valid. If you have no return value in your native method, you would use void where I used boolean. The other two entries, public and native, further define the method. Public allows the method to be called by other classes, and native tells Java that this is a native method, meaning that it was created in a language other than Java. Now that we've defined the method and made it a part of the ValCust class, let's look at the next JNI-related logic:

static
   {
     System.loadLibrary("CUSTOMER");
   }

The loadLibrary method loads the ILE service program, CUSTOMER, into Java. The service program must exist within your library list for the above command to work. If it can't find the service program, or if you already know that it won't exist in the library list, you can qualify it as follows:

static
   {
     System.loadLibrary("QSYS.lib/YOUR_LIB.lib/CUSTOMER.srvpgm");
   }

Now you're pretty well set. I've shown you how to define the native method to be part of the ValCust class and also how to load the service program, from which the native method will draw its logic. All that's left is to call the native method. To do that, you employ the following code:

     boolean found;
     String custID = new String(argv[0]);
     ValCust vc = new ValCust();
     found = vc.checkCust (custID.getBytes());

The first line defines a Boolean variable called found. This will be used to house the return value. The second line defines a string called custID, which is initialized with the first parameter of the byte array data passed into this Java class when it was called from the command line. The next line creates an object instance of our ValCust class, called vc. This is done because the ValCust class is not a static class, meaning that it cannot be directly accessed; only objects created from the class can be accessed. It's kind of like a blueprint of a house. You can't live in the blueprint. It's only when you build a house (object) from the blueprint (class) that you can then move on in. Finally, the native method, checkCust, is a called, passing byte data converted from the custID string, using the getBytes method, and returning a Boolean indicator in the found variable.

Putting It All Together

Now you have a Java application that you can call, passing an 8-byte customer ID, and it will print a message stating whether or not that customer ID is valid. The logic for the customer lookup exists in the ILE service program, but that service program is loaded into Java, and its associated subprocedure is defined as a native method within the ValCust class. Then it can be called by Java using a standard Java method call. The following figure illustrates how to call the Java class and what happens when I pass valid and invalid customer IDs:

I have glossed over most of the Java code, but you should take a look at it and use it on your own. You can also check out my article "Java Concepts for iSeries Programmers," to get a better understanding of what the Java is doing.

Use with Caution

I have given you a high-level look at JNI and how to use it to call RPG from Java using native method calls. However, now that I have given you this awesome tool, I will also tell you that there are pros and cons to using it. One pro is simply that JNI is the standard way to call native methods and doesn't require any extra classes or JAR files outside of the standard Java Development Kit. Among the cons is performance hits, because of the JNI resolution that has to be done each time the native method is called. There is also a potential performance hit with the synchronization of module calls on the ILE side. Remember, RPG is thread-safe, but it is not thread-enabled. OS/400 will handle the synchronization of multiple threads, provided you coded Thread(*SERIALIZE) in your service program. This means that simultaneous calls to the same module, within a single job, cause an increase in synchronization, which will subsequently slow down each thread's completion time. Remember when I stated that JNI still might not be the best way to access RPG from Java? Well, IBM recommends using JNI only when there is no alternative. Or, if you have to use it, it is best used when the service program has to do a considerable amount of work and will not be called all that often. So if you can't use the iSeries Toolbox for Java or JDBC to accomplish your task, and rewriting your business logic in Java is out of the question, JNI is just the ticket.

Kevin Vandever is a lead IT engineer at Boise Cascade Office Products in Itasca, Illinois, and a co- editor of Midrange Programmer, OS/400 Edition. He can be reached at kvandever@itjungle.com.

Sponsored By
WORKSRIGHT SOFTWARE

Indiana and Arkansas have new area codes! How are you going to update your customer files?

We have the answer. Our ZIP/CITY System for the AS/400 can automatically update your customer files for this area code split and future ones as well.

Visit our Web site www.worksright.com to learn more about ZIP/CITY. We offer a free, no-hassle, 30-day trial. Phone, fax, e-mail us, or order your free trial directly from our Web site.

THIS ISSUE
SPONSORED BY:
Help/Systems
SoftLanding Systems
BCD Int'l
Jacada Ltd.
Profound Logic Software
WorksRight Software
BACK ISSUES
TABLE OF CONTENTS
The Basics of ILE Service Programs
Basic HTML: Headers, Bodies, and Flying Text
Getting Acquainted with Grep
JSP Server-Side Error Handling, Part 1
How Does Your Browser Find a Web Page?
Calling RPG Native Methods from Java
  Newsletters | Subscribe | Advertise | About Us | Contact | Search | Home  
  Last Updated: 2/13/02
Copyright © 1996-2008 Guild Companies, Inc. All Rights Reserved.