mercury worlds

Three Ways I Called an RPGLE Program From Java

| Comments

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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  // 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.

SQL stored procedure definition
1
2
3
4
5
6
7
8
create procedure rtvgljseq
(in location char(5),
 out newseq integer)

external name incgljseq
language rpgle
parameter style general
;

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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void main(String[] args) {

  // Exerciser program.
  Object[] parms = new Object[5];

  // parms[0] receives the output, nextSequence
  parms[1] = "192.168.0.1"; // IP address of IBM i system
  parms[2] = "USERID"; // user id to sign on
  parms[3] = "PASSWORD"; // password for user id
  parms[4] = "LOC"; // location from which to retrieve the next sequence

  try {
      System.out.println("Input : '" + parms[4] + "'");
      execute(parms, null, null);
      System.out.println("Output: '" + parms[0] + "'");
  }
  catch (Exception e) {
      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.

RPGLE call using ProgramCall
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public static synchronized void execute(Object[] parms, StateTableInstructionState stis, MethodMessages mm) {

  int nextSequence = 0;
  String sysname = (String) parms[1];
  String userid = (String) parms[2];
  String password = (String) parms[3];
  String vanguardLocation = (String) parms[4];
  AS400 sys = new AS400(sysname, userid, password);

  try {
      String msgId, msgText;

      // Set up library list for program call.
      CommandCall command = new CommandCall(sys);
      if (command.run("CHGLIBL LIBL(PROD_MOD VNGDBDTA)") != true) {
          AS400Message[] messageList = command.getMessageList();
          for (int i = 0; i < messageList.length; i++) {
              msgId = messageList[i].getID();
              msgText = messageList[i].getText();
              System.err.println(msgId + " - " + msgText);
          }
      }

      // Create field types for parameters.
      AS400Text txt5 = new AS400Text(5);

      // Create parameter array and populate.
      ProgramParameter[] parmList = new ProgramParameter[2];
      parmList[0] = new ProgramParameter(txt5.toBytes(vanguardLocation));
      parmList[1] = new ProgramParameter(4);

      // Set up program call and run.
      ProgramCall pgm = new ProgramCall(sys, "/QSYS.LIB/PROD_MOD.LIB/INCGLJSEQ.PGM", parmList);
      if (pgm.run() != true) {
          AS400Message[] messageList = pgm.getMessageList();
          for (int i = 0; i < messageList.length; i++) {
              msgId = messageList[i].getID();
              msgText = messageList[i].getText();
              System.err.println(msgId + " - " + msgText);
          }
      } else {
          AS400Bin4 bin4Converter = new AS400Bin4();
          byte[] data = parmList[1].getOutputData();
          nextSequence = bin4Converter.toInt(data);
      }
  } catch (Exception e) {
      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.

PCML for INCGLJSEQ
1
2
3
4
5
6
7
8
9
10
<pcml version="4.0">
   <!-- RPG program: INCGLJSEQ  -->
   <!-- created: 2009-10-30-13.26.10 -->
   <!-- source: LOYD/SOURCE(INCGLJSEQ) -->
   <!-- 18 -->
   <program name="INCGLJSEQ" path="/QSYS.LIB/LOYD.LIB/INCGLJSEQ.PGM">
      <data name="INLOCATION" type="char" length="5" usage="input" />
      <data name="OUTUSESEQ" type="int" length="4" precision="31" usage="inputoutput" />
   </program>
</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.

RPGLE call using PCML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public static synchronized void execute(Object[] parms, StateTableInstructionState stis, MethodMessages mm) {

  int nextSequence = 0;
  String sysname = (String) parms[1];
  String userid = (String) parms[2];
  String password = (String) parms[3];
  String vanguardLocation = (String) parms[4];
  AS400 sys = new AS400(sysname, userid, password);

  try {
      String msgId, msgText;
      // Set up library list for program call.
      CommandCall command = new CommandCall(sys);
      if (command.run("CHGLIBL LIBL(PROD_MOD VNGDBDTA)") != true) {
          AS400Message[] messageList = command.getMessageList();
          for (int i = 0; i < messageList.length; i++) {
              msgId = messageList[i].getID();
              msgText = messageList[i].getText();
              System.err.println(msgId + " - " + msgText);
          }
      }
      ProgramCallDocument pcml = new ProgramCallDocument(sys, "incgljseq");
      pcml.setValue("INCGLJSEQ.INLOCATION", vanguardLocation);
      pcml.setValue("INCGLJSEQ.OUTUSESEQ", nextSequence);
      if (pcml.callProgram("INCGLJSEQ") != true) {
          AS400Message[] messageList = pcml.getMessageList("INCGLJSEQ");
          for (int i = 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 (Exception e) {
      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.

RPGLE call using JDBC
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static synchronized void execute(Object[] parms, StateTableInstructionState stis, MethodMessages mm) {

  int nextSequence = 0;
  String sysname = (String) parms[1];
  String userid = (String) parms[2];
  String password = (String) parms[3];
  String vanguardLocation = (String) parms[4];

  try {
      DriverManager.registerDriver(new com.ibm.as400.access.AS400JDBCDriver());
      Connection conn = DriverManager.getConnection("jdbc:as400://" + sysname + ";naming=system; libraries=,prod_dta,prod_mod,vngdbdta", userid, password);
      CallableStatement cs = conn.prepareCall("call rtvgljseq(?,?)");
      cs.registerOutParameter(2, java.sql.Types.INTEGER);
      cs.setString(1, vanguardLocation);
      cs.execute();
      nextSequence = cs.getInt(2);
      cs.close();
  }
  catch (Exception e) {
      System.err.println(e.getMessage());
      e.printStackTrace();
  }
  parms[0] = Integer.toString(nextSequence);
}

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.

Links:

Comments