If the only tool you have is a hammer, you tend to see every problem as a nail.
—Abraham Maslow
You may have noticed a kinder, gentler Joe Pluta for some time now. Maybe not, but in either case, I think it's time that I reach up to the top shelf of the closet and shake the dust off of the "Grumpy Old Programmer" persona. There's only so much guff I can take, and lately there's been a lot of guff out there. Frankly, some lines need to be drawn in the sand, and you know that I'm just the person to do it.
This article will be a case in point. In it, I am going to squarely position myself on an issue that is purely common sense to me, yet some very esteemed pundits out there are selling a viewpoint I can most charitably describe as painfully myopic.
What Am I Grumbling About?
So what exactly is it I'm grumbling about? I'm grumbling about the constant denigration of RPG as some sort of second-class language. One group would have you rewrite everything in Java; the other would grudgingly allow RPG but only after you have replaced all native I/O with SQL. And if you look at it carefully, since the primary I/O interface of Java is Java Database Connectivity (JDBC), which is simply a Java veneer over SQL's client level interface (CLI), then you quickly realize that both camps are saying the same thing: Down with native I/O! SQL must replace all other database access!
This opinion is insane. I mean that literally, in the sense that the definition of insanity is doing the same thing over and over and expecting different results. This idea that SQL must completely replace all RPG native I/O is simply silly. It's the same outrageous kind of statement that computer zealots make whenever a new technology hits "fad" stage, and they've been proven wrong every time. Let's look at some examples.
The claim: All server-side code must be replaced by client/server designs. The problem: Client/server code is more expensive and much harder to administer than server-based logic. The reality: Client/server code is used where it makes business sense, but more often business rules reside on the server, especially for integrated database transactions (this is one reason that the browser has become so prevalent so quickly; there is no software to administer on the client).
The claim: All green-screens must be replaced by GUIs. The problem: GUIs are slower, more expensive, and more difficult to program. The reality: Green-screens are still in use for data entry tasks the world over.
The claim: All procedural code must be replaced by object-oriented (OO) languages. The problem: OO code has a high upfront cost and is particularly difficult to modify to meet changing external demands. The reality: OO is used most often for tasks with strict, relatively stable task sets, and procedural code is still the best choice for business rules (hence the concept of "Procedural Java").
I won't go on, but you get the idea. My point should be clear by now: Change in computer programming is evolutionary, not revolutionary. While a new technique, technology, syntax, or architecture may indeed provide some benefit to programming, there hasn't been a single enhancement that has entirely wiped out the old way of doing things. Over time, certainly, old technology gets phased out. In the '70s, we wrote a lot of code in assembly language, particularly communications code. The Pascal and C compilers just didn't generate code that was tight enough to use in an interrupt handler. Today, I know very few people who write in assembly anymore; high-level languages (and more importantly, compilers) have simply gotten so good over the years that there are virtually no situations where the business decision favors the use of assemblers.
Similarly, there may be a day when SQL engines get so smart that SQL statements are incredibly easy to write and nearly always outperform the corresponding native I/O. I don't know if that day will ever come; it requires significant human ingenuity to write code to traverse complex databases. But I can say with absolutely certainty that today is not that day. And because SQL (the HLL for databases) isn't yet ready, we still need the assembly language of databases: RPG. And with that, I can finally focus on today's topic, calling RPG from Java.
Whoa! Brain Freeze!
Yeah, I know, that was a bit of a curve ball. But really, it follows logically. This is the "Weaving WebSphere" column, which is tasked to provide you, gentle reader, with the information required to make use of the WebSphere product line. Often, I focus on WebSphere Development Studio Client (WDSC) because it's such an important tool, but I don't like to forget the granddaddy of them all, WebSphere Application Server (WAS). While WDSC allows you to create wonderful, powerful browser-based applications using all the tools of the iSeries, WAS is the product that lets you actually deploy these applications, and it's good to return to those roots once in a while.
WAS is a J2EE Web application server, which means it supports servlets and JavaServer Pages (JSPs). I want to focus on using one specific architecture—JSP Model II—to take advantage of two of the great strengths of the iSeries: WAS and RPG.
Figure 1: This is the classic JSP Model II architecture diagram. (Click image to enlarge.)
In Figure 1, you see that the model talks to the database. Whether we adhere strictly to this design or cheat a little bit and let the controller do some access of the database, in either case we're talking about Java. And while Java is a great language for formatting and presenting HTML to the user, it has serious deficiencies when it comes to business rules processing. So what is really needed is the ability to allow either the controller or the model to use RPG to process business logic while still taking advantage of the UI formatting capabilities of JSP Model II. To do that, you need to be able to somehow call RPG programs from Java.
Ah Ha! Now We See What You're Up To
That's right, in this column, I will focus on the various methods we can use to invoke RPG programs from Java. IBM provides a number of ways to directly invoke RPG from Java, and there are a couple of less obvious methods as well.
Calling All RPG!
So let me break down the list into the four primary methods of invoking RPG from Java:
- Java Toolbox calls
- Stored procedures
- Java Native Interface (JNI)
- Database triggers
Each of these has its place, although as you move down the list, the situations that justify each option get fewer and fewer. For example, the Java Toolbox calls can be used whether your Web application runs on the iSeries or on another machine entirely, while the JNI calls work only when the Web application server is on the same machine as the RPG program being called.
The Java Toolbox
The Java Toolbox method of calling RPG is by far my favorite, probably because I am such a huge fan of the Java Toolbox itself. More completely known as the IBM Java Toolbox for iSeries, two versions exist: the licensed program product (LPP) version that IBM ships with every iSeries, and the open-source version available from SourceForge. I believe that for the iSeries market, the latter version (also known as JTOpen) is the most important piece of open-source software written. It's certainly one of the largest and most sophisticated of any open-source projects short of the Apache projects or Linux itself.
You can use the toolbox to invoke RPG programs (in fact, any iSeries program, including i5/OS API programs) in one of three ways: manually coding the ProgramCall class, using a PCML document, or using the newer XPCML document. Let's assume a very simple program. The program, MYPGM in library MYLIB, takes three parameters: the opcode (1A), a customer number (10A), and a customer name (30A). In the following examples, the program will pass a "G" in the opcode and put a customer number in the second field, and the called program is expected to return the customer name.
ProgramCall Class
The ProgramCall class is perhaps the simplest and most "pure Java" technique available. You create a ProgramCall object by identifying three components: the host system, the program, and the parameter list. Once you've built all three of these, you can then call the program.
String ifsPgmName = "/QSYS.LIB/MYLIB.LIB/MYPGM.PGM";
ProgramParameter[] plist = {
new ProgramParameter(1),
new ProgramParameter(10),
new ProgramParameter(30),
};
ProgramCall pc = new ProgramCall(sys, ifsPgmName, plist);
AS400Text cvt1A = new AS400Text(1);
AS400Text cvt10A = new AS400Text(10);
AS400Text cvt30A = new AS400Text(30);
plist[0].setInputData(AS400Text.toBytes(opcode));
plist[1].setInputData(AS400Text.toBytes(custno));
pc.run();
String name = AS400Text.toObject(plist[2].getOutputData());
The code above is very straightforward. The first line identifies the host. I've left out things like IP address, user ID, and password—things you'll have to insert that are specific to your site. (Also, in the interest of brevity, I've left out all of the standard error checking. Typically, I surround host communications stuff with a big try/catch block.) The next line identifies the program, but it uses IFS naming syntax rather than the traditional library/file syntax. The third line creates the parameter list, identifying the three parameters of one, 10, and 30 characters. The fourth line ties them all together into a program call object named pc.
One disadvantage to the ProgramCall method is that you have to do your own EBCDIC-ASCII conversions. The good news is that the toolbox provides classes for that. The next three lines create Java String converters for each of the lines. These converters can be used in either direction—taking Java Strings to EBCDIC or vice versa.
The last lines actually set up and execute the call. The lines that call setInputData are using the converters to translate the Unicode data in the variables opcode and custno into arrays of EBCDIC bytes that the program can understand. The next line calls the program, and the last line converts the returned EBCDIC data back into a variable of type String for the Java program to process.
That's all there is to it! It's really not a ton of code, and it allows you to directly call the RPG programs you need. And if the called program does not set on *INLR, you can realistically make lots of call to these programs without any performance issues. And the real beauty of the approach is that you can be running your Java application on any machine in the network (including Windows or *nix machines), and they can still make direct calls to RPG programs on the iSeries. This enables some very powerful multi-tier designs.
PCML
Program Call Markup Language (PCML) is an interesting alternative to the work shown in the previous section. Rather than do all the work yourself of creating the program object and especially the program parameters (and doing all the conversions), you can use the toolbox, which provides classes that do most of the work for you. And all you have to do to use these classes is to define your program in a separate XML file using the PCML syntax.
As you can see, much of the definition information I had to hard-code in the Java program in the previous method is instead now specified in this XML document. The location of the program as well as the lengths and types of the parameters (and also their names!) are all detailed here in the PCML. Notice that the host information is not specified here in the PCML; that's still left to the Java code.
ProgramCallDocument pcml = new ProgramCallDocument(sys, "mypgm");
pcml.setValue("mypgm.opcode", opcode);
pcml.setValue("mypgm.custno", custno);
pcml.callProgram("mypgm");
String name = pcml.getValue("mypgm.custname);
The code to call the PCML is significantly simpler than the code required to use the ProgramCall class. While you still need to create a connection to the host (the first line of code), the programming is much simpler after that. Just create an object of type ProgramCallDocument identifying the system and the PCML document. Note that a single PCML document can contain definitions for multiple programs! This gets around one of my complaints, which is having too many additional objects. Once you've loaded the document, set the input parameters for the specified program using the setValue method (which performs the Java-EBCDIC conversions for you). Invoke the program and get the value out using the getValue method. The more I learn about this technique, the better I like it. I have some concern about the overhead of loading the XML, which actually has been addressed; the PCML folks were smart enough to allow you to be able to read in a serialized version of the XML, which means skipping all the parsing overhead. In an enterprise environment where you may have hundreds of programs, this is crucial.
XPCML
I've already been a curmudgeon once this article, so I'll try not to do so here. XPCML is an extended version of PCML. To its credit, it uses a schema and even allows you to extend the schema so that you can create more-specific rules. Since these are program calls, though, the parameter definitions shouldn't change, so the rules would have more to do with the data in the parameters. And most of the time, the data I use to call a program is pretty dynamic, so I'm just less sure of the upside here as opposed to a couple of very specific disadvantages.
First, it's very wordy. I don't know where the tendency toward big, ugly names took over, but I really wish it would stop. XPCML is a great example. Compare the following code and the second code listing, the XPCML and standard PCML versions of the same program call.
I don't know about you, but I'm not sure why "passDirection" is a better keyword than "usage." But I promised not to be a curmudgeon, so we'll move on. In general, the XPCML document allows a little better editing of the PCML specifications, especially when you define default values (something I don't have the room to do here).
Stored Procedures
The other widely used option for calling RPG is to use stored procedures. Stored procedures work in two ways. In one method, you can create a multiple-occurring data structure (MODS) or a data structure array (a data structure using the DIM keyword) and return that to the calling program as a result set. This would be the way to emulate a query function. The other option is to invoke a stored procedure using a prepared statement. The code below shows the basics of a stored procedure call.
CallableStatement pstmt =
conn.prepareCall("{call p_getCustName(?,?,?)}");
pstmt.setString(1, opcode);
pstmt.setString(2, custno);
pstmt.registerOutParameter(3, Types.VARCHAR);
pstmt.executeUpdate();
String custname = pstmt.getString(3);
As you can see, the stored procedure technique is sort of a hybrid between the ProgramCall and PCML techniques. You have to programmatically specify the parameter types, but once you do, the conversions happen automatically. Also, I left out one big piece, which involves defining the stored procedure to the SQL engine. The following code provides an example.
OPCODE IN CHAR (1),
CUSTNO INCHAR(10),
NAME OUT CHAR(30))
LANGUAGE RPG NOT DETERMINISTIC NO SQL
EXTERNAL NAME MYLIB.MYPGM
PARAMETER STYLE GENERAL
It's an interesting trade-off. I think for returning result sets (query operations), the stored procedure approach has definite merit. That's especially true in these days of being able to dynamically allocate memory; a dynamically allocated data structure array has a lot of flexibility. On the other hand, if what I want is to simply call a program, passing in a couple of parameters and getting a return value, I think the stored procedure approach has a little too much overhead for my taste; I'd lean toward either the ProgramCall or PCML technique.
JNI and Triggers
JNI and triggers are two other options, but they have very limited use. The JNI approach is a direct operating system call to a program. It can only work if the Java Web application is running on the same physical machine as the RPG program. There are a number of issues surrounding locking and performance, and in general it's a pretty messy way of doing things. On the other hand, it may be the best way to integrate applications that didn't originate on the iSeries. For example, UNIX applications ported to the Portable Application Solutions Environment (PASE) can be executed directly using this method.
Triggers are the least flexible way to communicate with RPG and are, for the most part, one way only. You can write a record to a database using any Java-enabled technique (JDBC, record-level access, you name it) and that write can initiate ("trigger") a call to an RPG program. However, there is no return data. The best that can happen is that the RPG program may be able to update another field in the written record, and the Java program can later retrieve the record and view the updated data. This is purely theoretical; I've never done this and wouldn't want to hazard a guess as to its performance. But it's certainly an option.
Nothing Stopping You
The point of this article, then, is that there is nothing stopping you from using RPG in conjunction with your Java-based Web applications. Even though your primary Web application server language is Java, you can invoke RPG programs for everything from simple queries to complex calculations to application logic. The tools exist today to fit in with whatever programming style you find most comfortable, from program calls to stored procedures. Remember, unless SQL gets a lot better at certain fundamental business functions, RPG is going to run rings around SQL for business rules. That being the case, using SQL for business logic is like trying to screw in a light bulb with a chainsaw.
Joe Pluta is the founder and chief architect of Pluta Brothers Design, Inc. and has been extending the IBM midrange since the days of the IBM System/3. Joe has used WebSphere extensively, especially as the base for PSC/400, the only product that can move your legacy systems to the Web using simple green-screen commands. He has written several books including E-Deployment: The Fastest Path to the Web, Eclipse: Step by Step, and WDSC: Step by Step. Joe performs onsite mentoring and speaks at user groups around the country. You can reach him at
LATEST COMMENTS
MC Press Online