Guild Companies, Inc.  
 
Midrange Programmer - How-To Advice & Free Code
OS/400 Edition
Volume 1, Number 2 - January 31, 2002

Prototyping and Calling Java Methods from RPG

by Kevin Vandever

In the last issue, Ted Holt explained the advantages of prototyping programs and modules ("RPG Prototyping"). He provided examples of how to use prototypes and showed a consistent way to call prototyped functions, regardless of their origin. I'm going to expand on that topic and show you some OS/400 V5R1 enhancements that enable RPG to define Java classes and call Java methods using the Java Native Interface (JNI). And, as you'll see, prototyping is right there in the middle of it all.

Defining Some Terms

So what are classes and methods? And why do we want to define them and call them in RPG? A method is a function or procedure that accomplishes a task. You can think of a method as being the same as an RPG subprocedure. A well-written subprocedure is a function that accomplishes a specific task. A class is a collection of methods and variables. You can compare a class to an iSeries service program, which is a collection of subprocedures (methods) and variables. They're not exactly the same, but that description is close enough to help you understand the concept. An object is a copy or instance of a class. Think of a class as the blueprint and the object as the actual house. You can have many houses built from the same blueprint, just as you can create many objects from one class.

The reasons we may want to define classes and call Java methods from within an RPG program are two- fold. First, the Java API set contains hundreds of classes, any one of which may be exactly what you are looking for to solve a particular problem. And having these classes already written and tested for you means that you don't have to reinvent the wheel each time your application needs a new function. Second, you or someone else in your shop may at some point develop your own home-grown Java applications that provide the functionality you need to be able to access from an RPG program. OK, enough theory. Let's get to it.

The Object Data Type

With V5R1 came the introduction of the object data type. The object data type is signified by placing an O in column 40 of your D-spec and implementing the new keyword CLASS in the keyword portion of the D- spec. The CLASS keyword contains two parameters. The first parameter is *JAVA, which simply identifies the object as a Java object. The second parameter is the fully qualified class name for your object. The class name is case-sensitive, so make sure you get the case correct when defining your class. Check out the following example, which defines a Java BigDecimal class:

D Sum             S               O   Class(*JAVA:'java.math.BigDecimal')

IBM does place some restrictions on object usage. You cannot use objects as subfields in a data structure. Also, though you can have an array of object fields, you cannot create pre-runtime or compile-time tables and arrays of object data types.

Proper Prototyping Promotes Positive Programming

OK, so you've defined an object. Great! Now what? Well, that's where the second Jave Native Interface- related V5R1 enhancement comes in. In last issue's prototyping article, Ted discussed the EXTPROC keyword and how you can use it to prototype ILE modules so that you can call them with the prototyped call, CALLP. IBM expanded the EXTPROC keyword in V5R1, making it possible to prototype a local Java method as well as an ILE module. The EXTPROC keyword now has three parameters used to prototype a Java method (EXTPROC has not changed in the case of ILE modules; you still use the one parameter, module name, as you did before). You'll recognize the first two parameters from the CLASS keyword in the previous paragraph. They are *JAVA and the qualified, case-sensitive class name that contains the Java method you are prototyping. The last parameter is the Java method name. Below is the prototype for the Java add method, which allows you to add two BigDecimal objects together and return the sum:

D add             PR              O   ExtProc(*JAVA:                    
D                                         'java.math.BigDecimal':       
D                                         'add')                        
D                                     Class(*JAVA:'java.math.BigDecimal') 
D  BigD2                          O   Class(*JAVA:'java.math.BigDecimal') 
D                                     Const                             

I know; it doesn't look much like any prototype you've seen before. Well, not only does it look a little different, it behaves differently, too. On the first line, I defined the prototype name, add. This is the same name as the Java method I am prototyping, but it doesn't have to be. You can call it whatever you want. The PR states that this is a prototype and the O data type signifies that the return value is an object. Next you have the EXTPROC keyword with its three parameters: *JAVA, the class name, and the method that you are prototyping--in this case, add. The next line is part of the definition of the return value. Remember I told you that, to define an object, you needed the O data type and the CLASS keyword? The same holds true here. The CLASS keyword completes the definition of the return value defined on the first line of the prototype. Next is the input parameter definition, BigD2. This is pretty straight forward now that you know the rules. This input parm is a Java object from the class java.math.BigDecimal. Since this method will not change the value of this parameter, I use the Const keyword to tell it so and make it a little more efficient.

You're set, right? What? Only one input parm? You thought the add method returned the sum of two BigDecimals? You are correct, and this is where Java methods act a little differently than prototyped programs, modules, or procedures. When prototyping ILE functions, you specifically define a parameter for every value you will pass to that function. But Java doesn't work that way. Java has the concept of THIS instance, meaning the instance of this object itself can contain a value; therefore, when calling the add method, you will pass it THIS instance of the object and one input parameter. It will return a BigDecimal object containing the sum. You'll see what I mean when I show you the call to the Java method.

The *CONSTRUCTOR Method

Before I move on to calling the add method, I want to first talk about constructors. Java classes contain constructor methods. These constructor methods allow you to create an instance of a particular class based on some input. This is important to know when using objects, especially in RPG. Think about what we are going to do. We are going to pass two BigDecimals to a method and return the sum. We already know how to create object data types in D-specs, but how do we instantiate (assign a value) to those objects? Well, there are two ways.

The first way is to define the object in a D-spec, as we will do with the sum object, and use that object to accept the return value from a Java method. That works wonderfully when the value for that object is going to be filled by the Java method, but if you want to instantiate the object with a value in your application, you must prototype a valid constructor method and call that method passing the appropriate non-object typed data. There is no MOVEOBJ op code, yet, that will do it for you.

In the case of the BigDecimal class, there are four constructor methods, and each one translates a particular object type into a BigDecimal. In my example, I will show you one: BigDecimal converted from a String. There are also constructors to translate from Double and two different types of BigInteger, but Sun Microsystems recommends translating a BigDecimal from a String, because it will produce more accurate data, since BigDecimal will translate exactly to the Strings value (.1 in String is .1 in BigDecimal); whereas data conversion issues may arise when you translate from a non-string object. The constructor method prototype for String to BigDecimal translation is the following:

D String2BigD     PR              O   ExtProc(*JAVA:                   
D                                         'java.math.BigDecimal':      
D                                         *CONSTRUCTOR)                
D                                     Class(*JAVA:'java.math.BigDecimal')
D  Str                            O   Class(*JAVA:'java.lang.String')  
D                                     Const                            

It's not much different than the previous prototype, except that, instead of a method name as the third parameter of the EXTPROC keyword, *CONSTRUCTOR is used. This method accepts a String and will return a BigDecimal that we can then pass to the add method. The Str parm will not be changed by the method, so I define it as a constant using the CONST keyword. There are a number of data conversion issues and rules for passing data to and from Java. Since we are starting with alphanumeric data in our RPG, we aren't too concerned with the rules. However, there will be times when you are. For a complete listing of RPG-to-Java data issues, check out Chapter 11 of the ILE RPG Programmer's Guide.

Calling the Java Method

Now we're ready to call our methods. I've listed the complete RPG used to do the trick. (You can download the RPG program as well as the prototype copybook.)

**********************************************************************
 * To Compile:                                                         
 *                                                                     
 *  CRTBNDRPG PGM(xxx/MATH) SRCFILE(xxx/QRPGLESRC)                     
 *                                                                     
 **********************************************************************
H DftActGrp(*NO) ActGrp(*CALLER)                                       
                                                                       
 /Copy *LibL/QRpgLeSrc,MathPr                                          
                                                                       
D alpha1          C                   Const('2.5')                     
D alpha2          C                   Const('1.5')                     
D string1         S               O   Class(*JAVA:'java.lang.String')  
D string2         S               O   Class(*JAVA:'java.lang.String')  
D Sum             S               O   Class(*JAVA:'java.math.BigDecimal')
D BigD1           S               O   Class(*JAVA:'java.math.BigDecimal')
D BigD2           S               O   Class(*JAVA:'java.math.BigDecimal')
D StringSum       S               O   Class(*JAVA:'java.lang.String')
D DisplaySum      S             30A   Varying                        
                                                                     
 *  Create String objects from the alphanumeric constants            
                                                                     
C                   Eval      String1 = newString(alpha1)            
C                   Eval      String2 = newString(alpha2)            
                                                                     
 *  Create BigDecimal Objects from the String objects. We            
 *  could creat BigDecimal directly from a float or bigInt           
 *  and save the String object creation step, but Sun                
 *  recommends using Strings for data accuracy.                     
                                                                     
C                   Eval      BigD1 = String2BigD(string1)           
C                   Eval      BigD2 = String2BigD(string2)           
                                 
  *  Add the two BigDecimal objects. See how there are two         
  *  parameters even though we only defined one. The first         
  *  parm is the instance parm and the second parm is the          
  *  one that we defined in the prototype.                         
                                                                   
 C                   Eval      Sum = add(BigD1:BigD2)              
                                                                   
  *  Now convert the BigDecimal sum back to a string; then         
  *  convert the String into aplha data for us to view.            
                                                                   
 C                   Eval      StringSum = BigD2String(Sum)        
 C                   Eval      DisplaySum = getBytes(StringSum)    
                                                                   
  *  display the alpha version of the sum and end the program.     
                                                                   
 C     DisplaySum    Dsply                                         
 C                   Eval      *InLr = *On                 

The first thing to mention is the H-spec. I tell the compiler from this spec that I don't want to use the default activation group, DFTACTGRP(*NO). Instead I will use the callers activation group, ACTGRP(*CALLER). Placing the directives in the H-spec will save you time and allow for more consistent compilation and running. Next you'll see the /copy statement, used to bring in my prototypes. Then I initialize two alpha constants; these are the two values I am eventually going to add together.

Let's jump to the C-specs and see how I call the Java methods. First, I convert my two alpha fields to Strings. Again, I could have converted float or integer data directly to BigDecimal, but I decided to take the extra step of converting to String in the name of accuracy. Now that my data has been translated to String objects, I will now translate those string objects to BigDecimals, using the String2BigD method. Now I have my data in the proper format, so I simply call the add method, which will add the two BigDecimal objects and return a BigDecimal object in sum. Notice that I have two parameters in the add method even though I only defined one. This is the instance object that I was talking about. Java uses the instance of the object itself as the first parameter, and the second parameter is the one we defined. Anyway, now I have my sum, but I can't really do anything with an object, so I need to convert it back to String and then to Alpha so that I can display it. I accomplish these tasks by calling the BigD2String and getBytes methods respectively.

JNI in RPG is A-OK

Of course, you don't have to go through all of this trouble just to add two numbers. The point is that you can access local Java APIs and home-grown applications interactively, in RPG, using prototyping and prototyped calls. I've given you a basic look at calling Java methods using JNI. I provided a simple enough example, so that you could concentrate on the concepts and not worry so much about the business rules. And you should definitely concentrate on the concepts. Make sure you understand the difference between classes, objects, and methods. Get comfortable with constructors, static versus non-static calls, interfaces as well as other object-oriented concepts. It will serve you well when using JNI.

In coming issues, I will provide a more advanced look at JNI; specifically, native method calls from Java, threading issues, Java Virtual Machine concerns, and much more. The wall between Java and RPG is starting to come crashing down. It's not quite there yet, but JNI has provided us with another hammer.

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

Sponsored By
SOFTLANDING SYSTEMS

Thinking HIGH AVAILABILITY?

Think SOFTWARE MANAGEMENT First!

80% of unplanned downtime is caused by Application Failure or Operator Error, not hardware failure, according to IBM’s iSeries 400 Availability Team.

Software Management is essential to keeping your applications available, reliable, and bug-free, no matter how often you update them. Let SoftLanding show you how. You'll finish software projects faster, with a higher degree of quality, and keep them online, using our industry-leading solutions for CHANGE MANAGEMENT, DEBUGGING, TESTING, DEPLOYMENT, DATABASE REORGS, and PROBLEM DIAGNOSIS & RESOLUTION.

High Availability through Software Management. For more info and FREE downloads, visit http://www.softlanding.com/products/400 or email info@softlanding.com.

THIS ISSUE
SPONSORED BY:
Help/Systems
SoftLanding Systems
BCD Software Int'l
Jacada Ltd.
RJS Software Systems
WorksRight Software
BACK ISSUES
TABLE OF CONTENTS
The Midrange Programmer Philosophy
Prototyping and Calling Java Methods from RPG
Coding SQL Functions in OS/400 V5R1
Subprocedures: Better than Subroutines
Five Cool Things You Can Do with OpsNav
Let Your Hair Down With Free-Formed C-Specs
  Newsletters | Subscribe | Advertise | About Us | Contact | Search | Home  
  Last Updated: 1/14/02
Copyright © 1996-2008 Guild Companies, Inc. All Rights Reserved.