Calling an IBM i Service Program using Java.

Recently, we have been working on a project to pull information from the IBM i using java. In order to do most of the heavy lifting we decided to use the JTOpen package. Using their documentation, it was quick and easy to set up a program call on the system and get the return values. I then attempted to switch over to calling a service program that fills out a variable passed in as an argument instead. JTOpen had plenty of documentation on their subset of ServiceProgramCall() but none that I could find on returning data though an IN/OUT argument. I would like to mention I am, most definitely, not a Java programmer BUT… here is how I did it and what I learned:

First, we must create the AS400 Object to work with. This takes in 3 Strings as its arguments (address, user, password).

// AS400 system creation
AS400 as400 = new AS400(“Address”, “User”, “Password”);

Next, I found I had to add the library to the users LIBL even though we are calling it directly. If I did not do this, I would get a “object does not exist” error. This does not make sense to my inexperienced mind… If you have more of an understanding of this, please let me know!! In order to do this I used CommandCall() to simply call “ADDLIBLE DEMOLIB”. As it is being called by the same user in the same connection the libl remains the same when we do the service call.

CommandCall cmd = new CommandCall(as400);
if (cmd.run("ADDLIBLE DEMOLIB") != true) {
   System.out.println("Command failed!");
}

Here, we create the ServiceProgramCall() object. It takes an AS400 Object as its argument.

//Service program call object
ServiceProgramCall testPgm= new ServiceProgramCall(as400);

Our service program we are calling takes in 4 arguments. (String that describes the system, the return variable(1000 bytes for now), Int for debug, Int for file handling)

// parameter list of 4 values... Type, Return, 0, 0
ProgramParameter[] parmlist = new ProgramParameter[4];

First parameter… We have to convert the String to a byte array and then to a AS400Text Object of length 4. I Believe this handles the conversion to EBCDIC but I could be wrong. Then we assign it to the parameter array.

//Type (*TST)
AS400Text sysSel = new AS400Text(4, as400);
byte[] systemSel = sysSel.toBytes("*TST");
parmlist[0] = new ProgramParameter(systemSel);

Here is where I had the most troubles. This is the variable we are going to fill out using the SRVPGM. When we first create it, we use the constructor with no arguments then manually set the Object variables. setOutputDataLength() is, fairly, self explanatory. We use this to set the variable size to 1000bytes. Next, we use setParameterType to dictate whether to pass by value or pass by reference. Passing an argument of (1) to this means pass by value, and (2) is pass by reference. I believe pass by value has a maximum size before you must pass by reference.

//Return value... length 1000 passed by reference
parmlist[1] = new ProgramParameter();
// parameter type 2 is by reference
parmlist[1].setOutputDataLength(1000);
parmlist[1].setParameterType(2);

This is where we create the final two parameters. We convert an Int to an AS400Bin4 and pass it the value of 0.

//Final two values will just get passed 0 
AS400Bin4 bin4 = new AS400Bin4();
Integer num = Integer.valueOf(0);
byte[] Number = bin4.toBytes(num);
parmlist[2] = new ProgramParameter(Number);
Integer uns_num = Integer.valueOf(0);
byte[] uns_Number = bin4.toBytes(uns_num);
parmlist[3] = new ProgramParameter(uns_Number);

We use the setProgram() function that takes 2 arguments (the path to the service program, and the parameters to pass). Even though we are using a direct path here, I found the library still needs to be added to the users LIBL.

//Set the program location, attach parameter list
testPgm.setProgram("/QSYS.LIB/DEMOLIB.LIB/TSTSRVPGM.SRVPGM", parmlist);

setProcedureName() is required to dictate the function to call with in the service program as there is no main().

//Set function name (get_test)
testPgm.setProcedureName("get_test");

I do not believe this is a required function call, but I have left it in for demonstration purposes. setReturnValueFormat() dictates the data type to be returned by the SRVPGM. As we are returning the data in the 2nd argument, no return value is required.

//Set return value to none
testPgm.setReturnValueFormat(ServiceProgramCall.NO_RETURN_VALUE);

Using the .run() we call the SRVPGM. The function, run(), will return a Boolean whether it was successful or not. If the call was not successful then we can use the getMessageList() function to pull the message queue for that job. If the call was successful, we have to convert the bytes returned in parameter 2 back into a String to work with in Java. First create an AS400Text object to put the bytes returned into. Then Cast that object into a String Object by using .toObject().

//Run the srvpgm
if (testPgm.run() != true) {
   AS400Message[] msgList = testPgm.getMessageList();
   System.out.println("The program did not run.  Server messages:");
   for (int i = 0; i < msgList.length; i++) {
      System.out.println(msgList[i].getText());
   }
} else {
   byte[] as400Data = parmlist[1].getOutputData();
   AS400Text ret = new AS400Text(1000);
   String output = null;
   output = ((String) ret.toObject(as400Data));
   result.setStatus(output);
}

Finally, kill the connection to the server.

//Disconnect from system
as400.disconnectService(AS400.COMMAND);

Most of this information I was able to find through the JTOpen documentation but a few bits and pieces I found it more beneficial to read through the constructors and function calls.

Cheers!

Charlie.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.