Java-Methoden aus RPGLE aufrufen
Hallo liebe Leserinnen und Leser,
in den vorangegangenen Blog-Artikeln haben wir uns damit beschäftigt, wie Java-Programme auf einer AS/400 (System-i) deployed und ausgeführt werden können. Angefangen bei der einfachen Ausführung über die QSH (Qshell) haben wir uns bis zu komplexeren Szenarien vorgearbeitet, bei denen wir ein Start-Skript und danach ein RPGLE-Programm zur Ausführung unseres Java-Programms verwendet haben. Jetzt gehen wir einen Schritt weiter und schauen uns an, wie wir Java-Methoden direkt aus einem RPGLE-Programm heraus aufrufen können.
Was ist Prototyping?
Bevor wir uns in den Code vertiefen, ein kurzer Exkurs, was „Prototyping“ eigentlich ist. In RPGLE ermöglicht uns das Prototyping, Funktionen oder Prozeduren mit einer definierten Schnittstelle aufzurufen. Damit können wir sicherstellen, dass wir die richtige Anzahl und Art von Parametern übergeben und zurückbekommen. Wenn wir dies auf externe Programme oder, wie in unserem Fall, Java-Methoden anwenden, sprechen wir ebenfalls von Prototyping. Es ist sozusagen die „Vertragsdefinition“ zwischen den beiden unterschiedlichen Welten von RPGLE und Java.
Was macht der Java-Code?
Der Java-Code gehört zum Paket `parmtest
` und definiert eine Klasse namens `ParmTest
`. Die Hauptfunktion `main
` ist für die Verarbeitung von Kommandozeilenargumenten zuständig. Wenn das Argument „list
“ ist, werden alle System-Properties ausgegeben. Andernfalls wird die angeforderte System-Property zurückgegeben. Zusätzlich gibt es Methoden wie `getProperty
`, `getProperties
`, und `getArrayLength
`, die auch von RPGLE aus aufgerufen werden können:
- `getProperty(String key)`: Gibt eine Zeichenkette zurück, die den Schlüssel und den zugehörigen Wert aus den Systemeigenschaften enthält.
- `getProperties()`: Liefert alle System-Properties als String-Array zurück.
- `getArrayLength(Object[] pArray)`: Ermittelt die Länge eines übergebenen Objektarrays.
Wie werden Java-Methoden in RPGLE eingebunden und aufgerufen?
Jetzt zum interessanten Teil: der RPGLE-Code. Wir sehen mehrere Prototypen für die Java-Methoden:
- `getBytes`: Holt die Byte-Repräsentation eines Java-Strings.
- `getArrayLength`: Ruft die Methode `getArrayLength` aus der `ParmTest`-Klasse auf.
- `makeString`: Erzeugt ein Java-String-Objekt.
- `createParmTest`: Erzeugt ein Objekt der Java-Klasse `ParmTest`.
- `getProperty`: Ruft die Methode `getProperty` in `ParmTest` auf.
- `getProperties`: Ruft die Methode `getProperties` in `ParmTest` auf.
Arbeitsablauf im RPGLE-Code:
- Klassenpfad und Umgebung: Mit `setCLASSPATH` wird die Umgebungsvariable `CLASSPATH` für Java gesetzt.
- Parameterverarbeitung: Der Schlüsselparameter `pKey` wird verarbeitet und als Java-String-Objekt `keyObj` gespeichert.
- Objekterstellung: Ein `ParmTest`-Objekt wird mit `createParmTest` erzeugt.
- Bedingungsprüfung und Methodenaufruf:
– Wenn der Schlüssel „list“ ist, wird `getProperties` aufgerufen, und alle System-Properties werden ausgegeben.
– Andernfalls wird `getProperty` aufgerufen, um eine bestimmte System-Property auszugeben.
Durch die Verwendung von Prototypen wird der Aufruf der Java-Methoden stark vereinfacht, und wir können die Stärken beider Sprachen in einem integrierten Programm nutzen.
Java-Code
package parmtest; import java.util.ArrayList; import java.util.Enumeration; import java.util.Properties; /** * * @author Guido Zeuner / MIT License */ public class ParmTest { /** * @param args the command line arguments */ public static void main(String[] args) { ParmTest parmTest = new ParmTest(); //Prüfen, ob ein Parameter übergeben wurde if (args.length > 0) { String arg = args[0].trim(); //Alle System-Properties anzeigen, wenn Parameter = "list" if (arg.equalsIgnoreCase("list")) { String[] results = parmTest.getProperties(); for(String result : results) { System.out.println(result); } } else { //Gewählte System-Property anzeigen, wenn Parameter = [property.name] System.out.println(parmTest.getProperty(arg)); } } else { //Mögliche Startparameter ausgeben System.out.println("Parameter [list] oder [property name]"); } } // Used from RPG and Java Code public String getProperty(String key) { return String.format("%s = %s", key, System.getProperty(key)); } // Used from RPG and Java Code public String[] getProperties() { ArrayList result = new ArrayList(); Properties env = System.getProperties(); Enumeration keys = env.keys(); while (keys.hasMoreElements()) { String key = (String) keys.nextElement(); String value = env.getProperty(key); result.add(String.format("%s = %s", key, value)); } String[] props = new String[result.size()]; result.toArray(props); return props; } // Used from RPG-Code to retrieve the length an Array public int getArrayLength(Object[] pArray) { int arrayLen = 0; if (pArray != null) { arrayLen = pArray.length; } return arrayLen; } }
RPGLE-Code
***************************************************************************** * Programm : TESTCALL1 * * Beschreibung : Starten von Java Prozessen mit Parametern * * * * erstellt am : 13.08.2017 von Guido Zeuner | MIT License * * * * Beispiel-Aufruf * * CALL PGM(TESTCALL) PARM('user.country') * ***************************************************************************** H DFTACTGRP(*NO) H ACTGRP(*CALLER) //Prototype-/Interface with Parameter to call this PGM D entry PR ExtPgm('TESTCALL1') D pKey 20a Const OPTIONS(*VARSIZE) D entry PI D pKey 20a Const OPTIONS(*VARSIZE) //Prototype for Calls to QCMDEXC D ExecCmd Pr ExtPgm('QCMDEXC') D Cmd 2048a Const D CmdLen 15 5 Const //Variable for Command-Output to QCMDEXC D CmdLength s 15 5 //Field to store a ShellCommand D shellCommand S 1024A varying //Field to store the length of a Parameter D parmLength s 10I 0 //Used for Output of the results via DSPLY D resultContent s 52a varying //Java-Classpath D CLASSPATH s 2048a varying //Prototype - get Value of s String D getBytes PR 65535A EXTPROC(*JAVA: D 'java.lang.String': D 'getBytes') VARYING //Prototype - get length of an Java Object Array D getArrayLength PR 10I 0 EXTPROC(*JAVA: D 'parmtest.ParmTest': D 'getArrayLength') D arrObj O CLASS(*JAVA: D 'java.lang.Object') D DIM(500) //Prototype - Constructor for java String from Byte array D makeString PR O EXTPROC(*JAVA: D 'java.lang.String': D *CONSTRUCTOR) D bytes 1024A CONST VARYING //Prototype - Constructor for java IntegerObj from INT(10) D makeInteger PR O EXTPROC(*JAVA: D 'java.lang.Integer': D *CONSTRUCTOR) D integer 10I 0 value //Prototype - Constructor for ParmTest.class D createParmTest PR O EXTPROC(*JAVA: D 'parmtest.ParmTest': D *CONSTRUCTOR) //Prototype - Method String getProperty(String key) in ParmTest.class D getProperty PR O CLASS(*JAVA: D 'java.lang.String') D EXTPROC(*JAVA: D 'parmtest.ParmTest': D 'getProperty') D keyObj O CLASS(*JAVA: D 'java.lang.String') //Prototype - Method String[] getProperties() in ParmTest.class D getProperties PR O CLASS(*JAVA: D 'java.lang.String') D DIM(500) D EXTPROC(*JAVA: D 'parmtest.ParmTest': D 'getProperties') //Field to store a property key as Java String Object D parmTestObj S O CLASS(*JAVA:'ParmTest') //Field to store the passed in property Key D key S 50A VARYING //Field to store a property key as Java String Object D keyObj S O CLASS(*JAVA:'java.lang.String') //Field to store the return result D result S 1024A VARYING //Field to store a result as Java String Object D resObj S O CLASS(*JAVA:'java.lang.String') //Field to store the ArrayLength D arrayLength S 10I 0 //Index for array Elements D arrayElement S 10I 0 //Field to store results as Java String Object D resArrayObj S O CLASS(*JAVA:'java.lang.String') D DIM(500) /free exSr setCLASSPATH; parmLength = %len(%trim(pKey)); key = %subst(pKey:1:parmLength); keyObj = makeString(key); parmTestObj = createParmTest(); if key = 'list'; resArrayObj = getProperties(parmTestObj); arrayLength = getArrayLength(parmTestObj:resArrayObj); for arrayElement = 1 to arrayLength; resObj = resArrayObj(arrayElement); if resObj <> *NULL; result = getBytes(resObj); resultContent = result; dsply resultContent; endif; endfor; exsr wait; else; resObj = getProperty(parmTestObj:keyObj); result = getBytes(resObj); resultContent = result; dsply resultContent; exsr wait; endif; *Inlr = *On; //Set Classpath ENV-VAR via QCMDEXC begsr setCLASSPATH; CLASSPATH = '/home/YOURLIB/ParmTest.jar'; shellCommand = 'ADDENVVAR ENVVAR(CLASSPATH) VALUE(''' + CLASSPATH + ''') REPLACE(*YES)'; CmdLength = %len(%trim(shellCommand)); ExecCmd(shellCommand:cmdLength); endsr; //DelayJob via QCMDEXC begsr wait; shellCommand = 'QSYS/DLYJOB DLY(2)'; CmdLength = %len(%trim(shellCommand)); ExecCmd(shellCommand:cmdLength); endsr; /end-free
ÜBERTRAGUNG AUF IBM I
Das vorhandene JAR-File (s. GitHub Repo) können wir auf einen IBM i Server übertragen. Eine einfache Möglichkeit ist die Verwendung von FTP:
- Öffnen Sie ein Terminal und starten FTP mit
ftp
. - Loggen Sie sich mit Ihrem Benutzernamen und Passwort ein.
- Navigieren Sie auf auf der AS/400 zu Ihrem
/home/ihruser
Verzeichnis mitcd /home/ihruser
. - Navigieren Sie auf Ihrem lokalen System in das Verzeichnis der Datei
ParmTest.jar
mitlcd /ihrpfad
- Wählen Sie den Dateiübertragungstyp
bin
- Übertragen Sie das JAR-File mit dem Befehl
put ParmTest.jar
.
Compilieren des RPGLE-Programms
CRTBNDRPG PGM(YOURLIB/TESTCALL1) SRCFILE(YOURLIB/QRPGLESRC) ACTGRP(*CALLER) DFTACTGRP(*NO) OPTION(*EVENTF)
Erläuterung:
PGM(LIBRARY/TESTCALL1)
: Damit legen Sie den Namen und den Speicherort für das kompilierte Programm fest.SRCFILE(LIBRARY/QSOURCE)
: Das ist der Speicherort der Source-Datei, die den RPG-Code enthält.ACTGRP(*CALLER)
: Dieser Parameter ist wichtig, weil er der im Code spezifizierten Aktivierungsgruppe entsprechen muss (ACTGRP(*CALLER)
).DFTACTGRP(*NO)
: Das deaktiviert die Verwendung der Default-Aktivierungsgruppe, entsprechend der DirektiveH DFTACTGRP(*NO)
im Code.
Beachten Sie, dass YOURLIB
ein Platzhalter für den Namen der Ihrer Bibliothek ist.
Dieser sollte natürlich angepasst werden, um CRTBNDRPG
korrekt aufzurufen.
OPTION(*EVENTF)
: Dies ermöglicht die Erzeugung einer Event-Datei, die zur Fehlerbehebung nützlich sein kann.
Aufruf des Java-Programms und des RPGLE-Programms
Sie können das Java-Programm auf verschiedene Arten aufrufen, um die Funktionalität zu testen:
- Auflisten aller System-Properties
- Java:
java -jar ParmTest.jar list
- RPGLE:
CALL PGM(YOURLIB/TESTCALL1) PARM('list')
- Java:
- Anzeige einer spezifischen System-Property, z.B.
user.country
- Java:
java -jar ParmTest.jar user.country
- RPGLE:
CALL PGM(YOURLIB/TESTCALL1) PARM('user.country')
- Java:
Beachten Sie, dass YOURLIB
ein Platzhalter für den Namen der Bibliothek ist, in der Ihr RPGLE-Programm gespeichert ist. Dieser sollte natürlich angepasst werden, um das Programm korrekt aufzurufen.
Bibliotheksnamen anpassen
Ein wichtiger Hinweis ist die Anpassung des Bibliotheksnamens. Wenn Sie den Code in Ihrer eigenen Umgebung ausführen, stellen Sie sicher, dass YOURLIB
durch den tatsächlichen Namen der Bibliothek ersetzt wurde, in der Ihr RPGLE-Programm abgelegt ist. Diese Anpassung ist entscheidend für die erfolgreiche Ausführung des Codes.
Alle Beispiele auf GitHub
https://github.com/gzeuner/RPG4J
Viel Spaß beim Ausprobieren.