Intégration des langages ILE/RPG4 et JAVA. ................................................................. : Vous devez être familliers avec les concepts suivants : : : : : ° ILE en général : : ° Les programmes de services et les fonctions : : ° Les prototypes en RPG4 : : ° Les bases du langage java et de la programmation Objets : : : :...............................................................: Il est possible de déclarer et d'utiliser depuis RPG4 : - des méthodes Java - des objets (retournés par Java ou destinés à être retournés) Il est aussi possible de déclarer en RPG4 : - des fonctions RPG4 devant être utilisées depuis java |
Quelques rappels : - java est un langage orienté objet - en java un objet est matérialisé par une Classe (CLASS en anglais) cette classe encapsule (contient et protège) + des variables, souvent privées c.a.d non visibles de l'exterieur - ces variables peuvent être des données élémentaires (octet, entier, numérique à virgule flottante, ..) - ou bien elles même des objets (String par exemple est un objet) + des méthodes (ou fonctions) chargée de traiter et/ou de retourner les variables sus nommées. Une de ces méthodes peut porter le même nom que la classe (on parle alors de constructeur) elle est chargée de réaliser l'initialisation lors de la création d'une instance de l'objet en mémoire. |
Déclaration d'un objet/java en RPG4 D javaChaine S O CLASS(*JAVA : 'java.lang.string') cette définition peut être placée - en tant que variable - en tant que paramètre d'une procédure - en tant que valeur retournée par une procédure Ici, l'objet javaChaine va probablement être chargé par l'appel à une méthode sous la forme javaChaine = maMethode() Déclaration d'une méthode D maMethode PR -> ? EXTPROC(*JAVA : 'nom de la classe' : / 'méthode' ou / *CONSTRUCTOR) / vous devez indiquer le type de donnée retourné |
par exemple, la methode suivante transforme l'objet String en suite d'octets : D StringversChainePR 32767 VARYING EXTPROC(*JAVA : 'java.lang.String' : 'getBytes') et celle ci transforme l'objet Date en Objet String D DateversString PR O CLASS(*JAVA : 'java.lang.String') EXTPROC(*JAVA : 'java.lang.Date' : 'toString') bien sur vous pourrez écrire : R = StringversChaine( DateversString(objDate) ) ; où objDate et R sont déclarés : D objDate S O CLASS(*JAVA : 'java.lang.Date') D R S 10A |
Appel et passage de paramètres : il existe deux types de méthode en Java. 1/ les méthodes statiques ou méthodes de classe, elles sont déclarées Static typeretour nomMethode() { } elle ne s'appuient pas sur une instance pour réaliser le traitement, vous devrez ajouter STATIC, lors du prototypage RPG. 2/ les méthodes qui s'appuient sur une instance (un objet en mémoire) VOUS DEVREZ AJOUTER un paramètre aux paramètre(s) déclaré(s) dans le prototype: le nom de l'objet instancié. -> en java : dat1.toString -> en RPG4 : DateversString(dat1) |
Exemple avec la méthode equalsIgnoreCase de l'objet String. Cette méthode compare deux objets String et indique (par un booléen) que leur contenu est identique (true) ou non (false) en java : public class Equals { public static void main(String[] args) { String maChaine1 = new String("test1"); String maChaine2 = new String("test2"); if (maChaine1.equalsIgnoreCase(maChaine2)) { System.out.println("identiques"); } else { System.out.println("différents"); } } } |
en RPG4 : H DFTACTGRP(*NO) ACTGRP(*CALLER) THREAD(*SERIALIZE) D newString PR O EXTPROC(*JAVA : 'java.lang.String': D *CONSTRUCTOR) D 30A CONST VARYING D maChaine1 S O CLASS(*JAVA : 'java.lang.String') D maChaine2 S O CLASS(*JAVA : 'java.lang.String') D StrEquals PR N EXTPROC(*JAVA : 'java.lang.String' : 'equalsIgnoreCase') D O CLASS(*JAVA : 'java.lang.String') /free maChaine1 = newstring('test1'); maChaine2 = newstring('test2'); if StrEquals(maChaine1 : maChaine2) ; dsply 'identiques' ; endif; *inlr = *on; /end-free |
Equivalence des différents types de données : +-----------------+------------------+----------------------------------+ + Java + RPG4 + commentaires + +-----------------+------------------+----------------------------------+ + Boolean + N + indicateur + +-----------------+------------------+----------------------------------+ + Byte + 3I 0 + un octet binaire + + + + (pas de conversion) + + + 1A + un caractère + + + + (conversion ASCII/EBCDIC) + +-----------------+------------------+----------------------------------+ + Byte[] + 1A DIM(x) + tableau d'octets + +-----------------+------------------+----------------------------------+ + Short + 5I 0 + entier court (deux octets) + +-----------------+------------------+----------------------------------+ + Int + 10I 0 + entier (quatre octets) + +-----------------+------------------+----------------------------------+ + Long + 20I 0 + entier long (8 octets) + +-----------------+------------------+----------------------------------+ + float + 4F + flottant (4 octets) + +-----------------+------------------+----------------------------------+ |
+-----------------+------------------+----------------------------------+ + Java + RPG4 + commentaires + +-----------------+------------------+----------------------------------+ + double + 8F + flottant (8 octets) + +-----------------+------------------+----------------------------------+ + Char + 1C + 1 octets en UCS-2 (unicode) + +-----------------+------------------+----------------------------------+ + Char[] + nC + n octets en UCS-2 (unicode) + +-----------------+------------------+----------------------------------+ + un objet + O + CLASS(*JAVA : ..) + +-----------------+------------------+----------------------------------+ + tableau + ? DIM(x) + pour pouvez faire un tableau + + + + de n'importe quelle définition + +-----------------+------------------+----------------------------------+ la plupart des chaines sont à taille variable : pour les variables caractères (UCS-2) indiquez VARYING Pour les tableaux de caractères, indiquez OPTIONS(*VARSIZE) Voyez ce superbe outil pour générer les prototypes RPG4 : http://www.foundation.be/webstart/generator/generator.jnlp |
Pour appeller une méthode java depuis RPG4 Nous l'avons vu, utiliser directement la méthode, ou bien CALLP si la méthode ne retourne pas de valeur (void). RPG va vérifier si la JVM (machine virtuelle Java, "run-time" java) est démarrée, sinon, il la démarre. Vous pouvez la démarrer par pgm (et l'arreter) en utilisant les API JNI. Java possède sont propre ramasse-miettes (méchanisme qui permet à la JVM de détruire les objets de la mémoire quand ils ne sont plus utilisés) Dans notre cas, Java ne sait plus, et nous devons donc supprimer les instances, nous même, toujours avec les API JNI. Enfin, si vous utilisez des classes non standard, précisez la CLASSPATH par : ADDENVVAR ENVVAR(CLASSPATH) VALUE('/mesclasses/:classes/autresclasses.jar') |
Vous pouvez fixer des options pour java, dans l'ordre suivant: Dans le fichier indiqué par la variable d'env. QIBM_JAVA_PROPERTIES_FILE Dans le fichier SystemDefault.properties de la HOMEDIR de l'utilisateur Dans le fichier SystemDefault.properties de /QIBM/UserData/JAVA400 dont java.class.path et java.version, pour la liste complète voyez http://publib.boulder.ibm.com/infocenter/iseries/v6r1m0/topic/rzaha/sysprop2.htm java.version n'est utilisée que pour les versions du JDK "classic", c'est à dire I5/OS, pour celles tournant sous PASE: 32 bits en 5.4, 32 ou 64 en 6.1 il faut renseigner JAVA_HOME (tjs variable d'environnement) si vous n'indiquez pas de version particulière, le système regarde les versions de JDK installées et choisi dans cette ordre : |
+--------------------------------------------------------------------------+ | Option de 57xxJV1 |java| JAVA_HOME | +--------------------------------------------------------------------------+ | 8 IBM tech. 5.0 32 bits | 1.5| /Qopensys/QIBM/ProdData/JavaVM/jdk50/32bit| +--------------------------------------------------------------------------+ | 9 IBM tech. 5.0 64 bits | 1.5| /Qopensys/QIBM/ProdData/JavaVM/jdk50/64bit| +--------------------------------------------------------------------------+ | 7 Classic 5.0 | 1.5| /QIBM/ProdData/Java400/jdk15 | +--------------------------------------------------------------------------+ |11 IBM tech. 6.0 32 bits | 1.6| /Qopensys/QIBM/ProdData/JavaVM/jdk60/32bit| +--------------------------------------------------------------------------+ |12 IBM tech. 6.0 64 bits | 1.6| /Qopensys/QIBM/ProdData/JavaVM/jdk60/64bit| +--------------------------------------------------------------------------+ |10 Classic 6 | 1.6| /QIBM/ProdData/Java400/jdk6 | +--------------------------------------------------------------------------+ | 6 Classic 1.4 | 1.4| /QIBM/ProdData/Java400/jdk14 | +--------------------------------------------------------------------------+ bien sur, les options non installées sont ignorées lors de la recherche, et vous pouvez toujours "forcer" une version par: ADDENVVAR ENVVAR(JAVA_HOME) VALUE('/Qopensys/QIBM/ProdData/JavaVM/jdk60/64bit') |
A l'inverse, pour lancer une fonction RPG4 depuis une classe JAVA vous devrez : 1/ coder le prototype avec EXPORT (fonction publique, visible) 2/ créér un programme de service et le placer dans *LIBL. 2/ déclarer votre programme de service en JAVA par : ............................................................. : static : : { : : System.loadLibrary("MONSRVPGM"); : : } : : : : (static) native boolean verifClient(byte nomDuClient[]); : :...........................................................: bien sûr il faut ensuite envoyer des arguments compatibles avec le type attendu, par exemple : if ( verifClient(str.getBytes() ) { // ne pas envoyer un String |
si vous souhaitez un code RPG4 simple, envoyez des types élémentaires (byte, int , etc...) si vous souhaitez un code JAVA simple, envoyez des objets et faites la conversion (par exemple String en Bytes) dans le programme RPG4. Exemple 1: Fonction RPG retournant la longueur d'une variable d'environnement. DgetenvvarL PR 10I 0 STATIC D EXTPROC(*JAVA : 'EnvClassL' : D 'getEnvVarL') D 128A VARYING CONST PgetEnvVarL B EXPORT D PI 10I 0 STATIC D param1 128A VARYING CONST ../... (suivent d'autres déclaration, voir l'exemple complet) |
/free envname = param1 ; envnamelg = %len(%trim(envname)) ; getenv(env : envlg : envvallg : envname : envnamelg : APIERR); // API de QTMHCGI, renseigne env retour = %len(%trim(env)); return retour; /end-free PgetEnvVarL E La classe JAVA class EnvClassL { static { System.loadLibrary("GETENVVARL"); } native static int getEnvVarL (byte envname[] ); |
public static void main(String[] args) { String envvar = args[0] ; int retour; retour = getEnvVarL(envvar.getBytes() ) ; System.out.println("variable " + envvar + " =" + retour ); } } Vous remarquerez l'envoi de données simples à l'aide de ".getBytes" A l'inverse, ce deuxième exemple utilise des données String (objet) le programme RPG4, doit donc transformer l'objet String en chaine simple (déclaration de la méthode getBytes), puis transformer la variable contenant les données à retourner en String (*CONSTRUCTOR sur la classe java.lang.string) |
DgetEnvVarO PR O EXTPROC(*JAVA : 'EnvClassO' : D 'getEnvVarO') D CLASS(*JAVA : 'java.lang.String' D STATIC D O CLASS(*JAVA : 'java.lang.String') * transformation String -> Bytes DgetBytes PR 128A EXTPROC(*JAVA : 'java.lang.String' D : 'getBytes') VARYING * transformation Byte -> String DnewString PR O EXTPROC(*JAVA : 'java.lang.String' D : *CONSTRUCTOR) D bytes 128A CONST VARYING PgetEnvVarO B EXPORT D PI O CLASS(*JAVA : 'java.lang.String') D STATIC D String1 O CLASS(*JAVA : 'java.lang.String') * variables D String2 S O CLASS(*JAVA : 'java.lang.String') ... |
/free envname = getBytes(String1); envnamelg = %len(%trim(envname)) ; getenv(env : envlg : envvallg : envname : envnamelg : APIERR) //APi de QTMHCGI String2 = newString(env); return String2; /end-free PgetEnvVarO E L'utilisation en JAVA est beaucoup plus simple (déclaration identique) native static String getEnvVarO (String envname ); public static void main(String[] args) { String envvar = args[0] ; String retour = getEnvVarO(envvar) ; System.out.println("variable " + envvar + " =" + retour ); } |