IBM i and XA license check

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

mjr_ibmi_license_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]

You have any questions?

Infor XA SystemLink with Node-RED