A recent project had me comparing different ways to call an RPGLE program using Java. I am a newbie / novice Java programmer, so this gave a lot of insight into the Java language and some of the JT400 / JTOpen tools.
To start, I had this RPGLE program. It retrieves, increments, sets, and returns a general ledger (GL) journal entry sequence number.
RPGLE program to get next GL sequence
1234567891011121314151617181920212223242526272829
// Program: incgljseq
// Retrieve last used journal entry sequence; increment and store;
// return new sequence.
Fdbaxrel0 UF E K DISK
d incgljseq pr extpgm('INCGLJSEQ')
d inlocation 5a const
d outuseseq 10i 0
d incgljseq pi
d inlocation 5a const
d outuseseq 10i 0
d sequence s 10i 0
/free
sequence = 0;
chain inlocation @axrecd;
if %found;
axlgnb += 1;
sequence = axlgnb;
update @axrecd;
endif;
outuseseq = sequence;
*inlr = *on;
return;
/end-free
Originally, I intended to call the program using a stored procedure. In the course of the project I decided to pursue calling the RPGLE program directly. In the end, I called the program using PCML, but would consider JDBC due to its brevity.
The integration tool I was using, Extol Business Integrator, has special parameter requirements when using Java programs a particular way. Because of this, I used the main() method as a testbed, enabling me to call the program from the command line, and later in Eclipse when developing the other calls.
I investigated three different ways to call the RPGLE program: using ProgramCall (call the program directly), ProgramCallDocument (call the program using PCML, which is like a prototyped call), and JDBC. The main method is the same for all three classes.
Java main() template
123456789101112131415161718192021
publicstaticvoidmain(String[]args){// Exerciser program.Object[]parms=newObject[5];// parms[0] receives the output, nextSequenceparms[1]="192.168.0.1";// IP address of IBM i systemparms[2]="USERID";// user id to sign onparms[3]="PASSWORD";// password for user idparms[4]="LOC";// location from which to retrieve the next sequencetry{System.out.println("Input : '"+parms[4]+"'");execute(parms,null,null);System.out.println("Output: '"+parms[0]+"'");}catch(Exceptione){System.out.println(e.toString());e.printStackTrace();}}
For all three classes, the execute() method calls the RPGLE program and returns the next sequence. First, here is the call using ProgramCall.
publicstaticsynchronizedvoidexecute(Object[]parms,StateTableInstructionStatestis,MethodMessagesmm){intnextSequence=0;Stringsysname=(String)parms[1];Stringuserid=(String)parms[2];Stringpassword=(String)parms[3];StringvanguardLocation=(String)parms[4];AS400sys=newAS400(sysname,userid,password);try{StringmsgId,msgText;// Set up library list for program call.CommandCallcommand=newCommandCall(sys);if(command.run("CHGLIBL LIBL(PROD_MOD VNGDBDTA)")!=true){AS400Message[]messageList=command.getMessageList();for(inti=0;i<messageList.length;i++){msgId=messageList[i].getID();msgText=messageList[i].getText();System.err.println(msgId+" - "+msgText);}}// Create field types for parameters.AS400Texttxt5=newAS400Text(5);// Create parameter array and populate.ProgramParameter[]parmList=newProgramParameter[2];parmList[0]=newProgramParameter(txt5.toBytes(vanguardLocation));parmList[1]=newProgramParameter(4);// Set up program call and run.ProgramCallpgm=newProgramCall(sys,"/QSYS.LIB/PROD_MOD.LIB/INCGLJSEQ.PGM",parmList);if(pgm.run()!=true){AS400Message[]messageList=pgm.getMessageList();for(inti=0;i<messageList.length;i++){msgId=messageList[i].getID();msgText=messageList[i].getText();System.err.println(msgId+" - "+msgText);}}else{AS400Bin4bin4Converter=newAS400Bin4();byte[]data=parmList[1].getOutputData();nextSequence=bin4Converter.toInt(data);}}catch(Exceptione){System.err.println(e.getMessage());e.printStackTrace();}sys.disconnectAllServices();parms[0]=Integer.toString(nextSequence);}
Don’t worry about the execute() method declaration; that is special for Extol Business Integrator (EBI). Also disregard the conversion of nextSequence from integer to string; again, this is to work around an issue I had with EBI.
The ProgramCall code has lots of parts: conversions from Java to IBM i (AS/400) data types and back, setting up the library list, and handling for retrieving IBM i error messages if necessary.
A little shorter way is to call using PCML. PCML is a tagged document similar to XML which is a “prototype” for calling an RPGLE program from Java. For more information about compiling a program to generate the PCML file see the post.
The PCML file defines the interface to the called program, including the program’s location, and number and type of parameters. The PCML’s file name is incgljseq.pcml.
And here is the Java code to call using PCML. We do not have to give the PCML’s extension when passing the file name into ProgramCallDocument. Under the covers, the method looks for a serialized version with extension .pcml.ser, or the regular file with extension .pcml.
publicstaticsynchronizedvoidexecute(Object[]parms,StateTableInstructionStatestis,MethodMessagesmm){intnextSequence=0;Stringsysname=(String)parms[1];Stringuserid=(String)parms[2];Stringpassword=(String)parms[3];StringvanguardLocation=(String)parms[4];AS400sys=newAS400(sysname,userid,password);try{StringmsgId,msgText;// Set up library list for program call.CommandCallcommand=newCommandCall(sys);if(command.run("CHGLIBL LIBL(PROD_MOD VNGDBDTA)")!=true){AS400Message[]messageList=command.getMessageList();for(inti=0;i<messageList.length;i++){msgId=messageList[i].getID();msgText=messageList[i].getText();System.err.println(msgId+" - "+msgText);}}ProgramCallDocumentpcml=newProgramCallDocument(sys,"incgljseq");pcml.setValue("INCGLJSEQ.INLOCATION",vanguardLocation);pcml.setValue("INCGLJSEQ.OUTUSESEQ",nextSequence);if(pcml.callProgram("INCGLJSEQ")!=true){AS400Message[]messageList=pcml.getMessageList("INCGLJSEQ");for(inti=0;i<messageList.length;i++){msgId=messageList[i].getID();msgText=messageList[i].getText();System.err.println(msgId+" - "+msgText);}}else{nextSequence=(Integer)pcml.getValue("INCGLJSEQ.OUTUSESEQ");}}catch(Exceptione){System.err.println(e.getMessage());e.printStackTrace();}sys.disconnectAllServices();parms[0]=Integer.toString(nextSequence);}
Finally, here is the call using JDBC, using the stored procedure definition listed above. It has by far the fewest lines of code, and eliminates the need for multiple conversions from Java types to IBM i types. It also elminiates the need of a separate call for setting the library list. (This can be done by calling a CL program wrapper, but this exercise is calling the RPGLE program.) One disadvantage of JDBC is being limited to using “simple” parameter types. Contrast with the PCML call, which can use complex parameters, such as RPG data structures.
My preference is to use either PCML or JDBC. The JDBC code is very brief and hides or elminiates variable casting. However, it is really suitable for simple parameter types, and requires an extra IBM i object (the stored procedure definition). PCML is nice because like JDBC, it describes the number and kind of parameters. However, there is a separate system connection and call to set the library list if needed. Like JDBC an extra object is needed (the PCML file) but rather than server-side, it is bundled with the Java code.
This post pulled together and briefly compared various ways to call an ILE RPG program from Java.