The challenge
You want to check how many users are using a license of your IBM I product and especially Infor XA? And see also the users? This is how it works with a short and simple Java program.
The key to achieve that, is to use the IBM i API QLZARTV which delivers all needed information.
Please note: this is just a example and comes without any warranty
Result of the check

Here is the Java code.
// LicenseCheck.java by MJR GmbH, Knittlingen, Germany
//
//Sample code to read license usage of IBM i
//
// by Demian Welker and Michael Raber (kindly supported by ChatGPT ;-)
//
// copy this file as LicenseCheck.java in your IFS folder and call the compile-and-start.sh script locally on IBM i from the same folder
import com.ibm.as400.access.*;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Arrays;
public class LicenseUsers {
static final Charset EBCDIC = Charset.forName("Cp037");
public static void main(String[] args) throws Exception {
System.out.println("Starting LicenseUsers...");
if (args.length < 1) {
System.out.println("Usage: java LicenseUsers <productId7> [release6|*ONLY] [feature4]");
System.out.println("Example: java LicenseUsers 5U33M7X V3R1M0 5001 for XA R11");
return;
}
String productId = pad(args[0], 7);
String release = pad(args.length > 1 ? args[1] : "*ONLY", 6);
String feature = pad(args.length > 2 ? args[2] : "5001", 4);
AS400 as400 = new AS400("localhost");
byte[] receiver = callQLZARTV(as400, 8192, productId, release, feature);
int bytesAvail = bin4(receiver, 4);
if (bytesAvail > receiver.length) {
receiver = callQLZARTV(as400, bytesAvail, productId, release, feature);
}
printResult(receiver);
as400.disconnectAllServices();
}
static byte[] callQLZARTV(AS400 as400, int receiverLength,
String productId, String release, String feature) throws Exception {
ProgramCall pc = new ProgramCall(as400);
pc.setProgram("/QSYS.LIB/QLZARTV.PGM");
byte[] receiver = new byte[receiverLength];
byte[] productIdent = ebcdic(productId + release + feature); // LICP0100 = 17 bytes
byte[] errorCode = new byte[272];
putBin4(errorCode, 0, errorCode.length); // bytes provided
putBin4(errorCode, 4, 0); // bytes available
ProgramParameter[] parms = new ProgramParameter[] {
new ProgramParameter(receiverLength), // receiver variable
new ProgramParameter(bin4(receiverLength)), // receiver length
new ProgramParameter(ebcdic(pad("LICR0300", 8))),
new ProgramParameter(productIdent),
new ProgramParameter(ebcdic(pad("LICP0100", 8))),
new ProgramParameter(errorCode, errorCode.length)
};
pc.setParameterList(parms);
if (!pc.run()) {
for (AS400Message m : pc.getMessageList()) {
System.err.println(m.getID() + " " + m.getText());
}
throw new RuntimeException("QLZARTV failed");
}
byte[] errOut = parms[5].getOutputData();
int errAvail = bin4(errOut, 4);
if (errAvail > 0) {
String msgId = text(errOut, 8, 7);
throw new RuntimeException("QLZARTV API error: " + msgId);
}
return parms[0].getOutputData();
}
static void printResult(byte[] r) {
int bytesReturned = bin4(r, 0);
int bytesAvailable = bin4(r, 4);
int usageLimit = bin4(r, 8);
int usageCount = bin4(r, 12);
String usageType = text(r, 16, 2);
String complianceType = text(r, 18, 2);
String licenseTerm = text(r, 20, 6);
String releaseLevel = text(r, 26, 6);
int peakUsage = bin4(r, 52);
int userOffset = bin4(r, 96);
int userCount = bin4(r, 100);
int userRecordLength = bin4(r, 104);
int licenseUserLength = bin4(r, 108);
System.out.println("Release level : " + releaseLevel);
System.out.println("License term : " + licenseTerm);
System.out.println("Usage limit : " + usageLimit);
System.out.println("Usage count : " + usageCount);
System.out.println("Peak usage : " + peakUsage);
System.out.println("Usage type : " + usageType);
System.out.println("Compliance : " + complianceType);
System.out.println("Users : " + userCount);
System.out.println();
if (userOffset == 0 || userCount == 0) {
System.out.println("No current license users found.");
return;
}
System.out.printf("%-6s %s%n", "Uses", "License user");
System.out.println("------------------------------");
for (int i = 0; i < userCount; i++) {
int pos = userOffset + (i * userRecordLength);
if (pos + 4 > bytesReturned) break;
int usesHeld = bin4(r, pos);
String licenseUser = text(r, pos + 4, licenseUserLength);
System.out.printf("%-6d %s%n", usesHeld, licenseUser);
}
if (bytesAvailable > bytesReturned) {
System.out.println();
System.out.println("Warning: receiver was too small; not all data returned.");
}
}
static byte[] ebcdic(String s) {
return s.getBytes(EBCDIC);
}
static String text(byte[] b, int off, int len) {
return new String(Arrays.copyOfRange(b, off, off + len), EBCDIC).trim();
}
static String pad(String s, int len) {
if (s.length() > len) return s.substring(0, len);
return String.format("%-" + len + "s", s);
}
static byte[] bin4(int value) {
return ByteBuffer.allocate(4).putInt(value).array();
}
static void putBin4(byte[] b, int off, int value) {
byte[] x = bin4(value);
System.arraycopy(x, 0, b, off, 4);
}
static int bin4(byte[] b, int off) {
return ByteBuffer.wrap(b, off, 4).getInt();
}
}and the script to compile and start:
First line is calling the Java compiler and then in the second line the compiled program. This is a generic description to allow simply to copy and run on the IBM I without deep skills in Java on i. Of course you can use any IDE like IntelliJ, VSCode, Eclipse or whatever to build and run.
The Parameters are
- 5U33M7X as the product code of Infor XA. This can be different, depending on your license.
- V3R1M0 stands for Release 11. If you are running XAR10 please use V3R0M0 or V2R9M0 for XAR9
- 5001 is the feature code of XA (main application)
# compile and call the Java program # put this script in the folder where the java source code is located # replace the parameters of the second line as needed for your XA version javac -cp /QIBM/ProdData/HTTP/Public/jt400/lib/jt400.jar LicenseUsers.java java -cp .:/QIBM/ProdData/HTTP/Public/jt400/lib/jt400.jar LicenseUsers 5U33M7X V3R1M0 5001
[/vc_column_text]




