pause-café
destinée aux informaticiens sur plateforme IBM i.
Pause-café #26
Quelques nouvelles du front :
- APACHE/TOMCAT disponibles
- Webfacing
- fonctionne avec W.A.S 3.5 sur un serveur NT (bons temps de réponses)
- fonctionne avec TOMCAT (sous webfacing, exportez au format WAR).
- Iseries Access for the WEB (Client Access pour le WEB)
- l'accès aux informations 400 fonctionne (assez mauvais temps de réponses)
- cette partie pourrait tourner sous TOMCAT.
- Hostpublisher ne fonctionne pas !
- ne tournerait que sous WAS (et qui plus est, version 3.5).
- rumeurs de V5R20 en juillet (voir www.volubis.fr) et nouveau processeur REGATA (POWER4)!
Quelques rappels sur les fondamentaux du Web :
l'AS/400 propose maintenant deux serveurs WEB : IBM HTTP server et Apache.
Le but est de fournir au clients (Internet Explorer, Netscape, Opera etc...)
des pages HTML statiques ou dynamiques.
Le standard HTML est un langage SGML (comme UIM sur l'AS/400) qui est
interprété complétement par la navigateur sur le
poste client: par exemple voici un texte en <b>GRAS</b> dont une partie est <font
color="#FF0000"> s'affiche : voici un texte en GRAS dont une partie est rouge |
Très vite il a fallut insérer dans ces pages HTML des données
entreprise (commandes en attente, stock disponible etc...).
Pour cela, la norme initiale fut CGI, qui permet l'appel d'un programme sur
le serveur, chargé de générer du HTML en réponse.
La technique est assez rugueuse mais cela fonctionne (sur l'AS/400 avec des
programmes C ou RPG4).
Pour Utiliser CGI en RPG, vous disposez des API suivantes (GAP4 Uniquement)
|
Pour alléger l'écriture de pgm CGI, la plupart des plates-formes proposent des langages de script ou l'on mélange dans un fichier texte, du HTML, du code (propriétaire) et des ordres d'accès à la base.Il s'agit TOUJOURS de langages interprétés.
L'interpréteur est un pgm CGI déjà écrit traitant
ces fichiers, en fournissant au navigateur la page HTML après avoir remplacé
les ordres d'accès aux données par les données elle même.
Cela s'appelle PHP dans le monde linux (souvent associé à la base MYSQL), ASP pour Microsoft (avec IIS) et Net.Data avec les bases DB2 chez IBM.
Net.Data est basé sur la notion de section, une section représentant
soit un page HTML Exemple :
Affiche la liste des 50 premieres appellations vinicoles, trièes par nom.
|
Pour terminer, l'état de l'art est aujourd'hui de travailler dans une architecture N tiers, c'est à dire en découplant le serveur de traitement (les pgm souvent placés avec le serveur WEB) des données (pouvant être situées sur un serveur éloigné).
Cette technique est implémentée avec les serveurs d'application ou serveurs de servlet.
Il s'agit d'écrire des programmes JAVA s'exécutant sur le serveur
et non sur le poste client , le serveur d'application assurant le lien entre
le serveur WEB et la JVM (machine virtuelle java).Ces programmes java pouvant
être des classes autonomes (servlet) générant du
HTML.
ou contenus dans des pages JSP : pages HTML faisant références
à des objets externes [des beans].
Les pages JSP permettant d'intégrer du HTML (conçu par un graphiste) et du code JAVA (écrit par un développeur) dans un même fichier, ce mélange étant reconnu par les principaux éditeurs : Dreamweaver de Macromédia, IBM Websphere Studio, etc ....).
Si, depuis ces pages, vous souhaitez accèder aux données, vous utiliserez JDBC, qui est à Java ce que ODBC st à Windows.
Une autre norme vient compléter tout cela : EJB pour Entreprise Java Bean. Il s'agit d'objets métiers pouvant être situés sur des serveurs externes.D'ailleurs, il est possible de rechercher ces objets dans un annuaire LDAP et de concatcter le serveur proposant le service, au moment du besoin.(architecture distribuée : CORBA).
Ces EJB peuvent être des beans de session , l'objet est instancié au monent où votre code en a besoin et est spécifique à votre code, ou bien persistant (un peu comme un base de données) et accessible à tous. On parle alors de bean d'entité réputés plus lourds (en performmances).
Le support des EJB est un des grandes différences entre en serveur d'application comme TOMCAT (qui ne les supporte pas aujourd'hui) et Websphere Application Serveur (W.A.S).
Tous ces normes sont regroupées dans une plateforme générique ; J2EE.
TOMCAT (c'est le moteur de servlet d'Apache, plus légers que W.A.S)
le moteur de servlet peut s'éxécuter sous forme "inprocess" ou intégré au serveur Apache (on utilise alors JNI ou Java Native Interface) ou bien "outprocess", vous trouverez alors d'un job à part (dans QSSYWRK) dialoguant avec votre serveur web (sur une autre machine ?) via sockets IP.
regardons comment configurer un serveur "inprocess" (en utilisant toujours la configuration sur le port 2001)
choisissez ensuite
le plus simple, la première fois, est de suivre l'assistant (wizard) :
On vous indique que si des fichiers existent déja, il seront reconfigurés.
(à utiliser uniquement la première fois donc)
ici, vous devez choisir
la configuration de servlets ou de pages JSP autonomes
(que vous devrez placer dans les bons répertoires "à
la main")
l'utilisation de fichiers archives ou .war (c'est la même chose que les archives java .jar)
Point suivant, servlets (classes java) ou pages JSP (pages "html" utilisant des classes externes)
le magicien à configuré pour vous un répertoire webapps/app1, qui sera automatiquement considéré comme un alias par le serveur web (directive JKmount ... inprocess, dans httpd.conf) contenant deux sous répertoires:
- jsp pour vos pages html et vos pages JSP
- WEB-INF /classes pour vos fichier .class( java)
- vous pourrez placer dans WEB-INF vos fichiers web.xml de définition de servlets
ici, nous testons une page jsp
Aller à la Fin
A P I :
sous ce terme générique, se cachent des routines systèmes permettant un accès sûr à des couches basses du système d'exploitation.
Il existe des API pour réaliser une action :
+ QCMDEXC[passer une commande], QUSCMDLN[fenêtre avec ligne de commande]
Des API de retour d'information :
+ je connais le nom de l'élément et je veux avoir plus de détails.
Des API de liste :
+ donnez moi la liste des utilisateurs en session, la liste des membres d'un fichier, des zones d'un fichier, des spools, etc...
premier exemple: API QLICOBJD permet de modifier certaines infos d'un objet. paramčtres : - CHAR(10) bibliothčque en retour. - CHAR(20) objet ŕ modifier <--objet(10c)-><--biblio(10c)--> un nom un nom ou *LIBL,*CURLIB. - CHAR(10) type d'objet - CHAR(??) infos ŕ modifier, découpé comme suit. BIN(4) nombre d'infos ŕ modifier. puis x fois BIN(4) clé (détermine l'info ŕ modifier) BIN(4) lg de la nouvelle valeur CHAR(??) nouvelle valeur. - CHAR(??) code erreur.
Les différentes clés admises: ! Clé ! lg ! valeur ---------------------------------------------------------------------- ! 1 ! 30 ! Fichier source <--fich(10c)-><-bib(10c)-><-mbr(10c)-> ! ! ! ! 2 ! 13 ! date de modif du source (format C YY MM DD HH MM SS) ! ! ! ! 3 ! 13 ! compilateur <-nom-du-produit(7c)-><-version(VxRyMz)-> ! ! ! (5722xx1) ! 4 ! 8 ! niveau de contrôle ! ! ! ! 5 ! 13 ! programme sous licence (idem compilateur) ! ! ! ! 6 ! 7 ! PTF (format XXzzzzz XX = préfixe zzzzz = n° ptf) ! ! ! ! 7 ! 6 ! APAR (n° d'analyse des incidents sur ce pgm /Azzzzz) ! ! ! ! 8 ! 1 ! cet objet peut-il ętre modifié par cette API 0=non ! ! ! 1=oui ! ! ! ! 9 ! 10 ! attribut défini par l'utilisateur. ! ! ! ! 10 ! 50 ! texte decriptif. ! ! ! ! 11 ! 1 ! remise ŕ zéro du compteur nb de jours d'utilisation ! ! ! 0=non , 1=oui. ! ! ! ! 12 ! 4 ! ID langage associé ŕ ce produit (29xx) ! ! ! ! 13 ! 4 ! option d'un logiciel sous licence 0=*BASE, 01 ŕ 99 ! ! ! ! 14 ! 4 ! ID composant (laissé ŕ votre imagination) ----------------------------------------------------------------------
exemple de CL renommant le source d'un objet et mettant ŕ jour l'objet /* RNMOBJSRC */ RNMM FILE(&SRCL/&SRCF) MBR(&SRCM) + NEWMBR(&NEWNAME) /* API QUI PERMET DE MODIFIER LES INFOS DANS L'OBJET */ CHGVAR %BIN(&CHGINFO 1 4) 1 /* NBR DE MODIFS */ CHGVAR %BIN(&CHGINFO 5 4) 1 /* CLE 1 = MODIF SOURCE */ CHGVAR %BIN(&CHGINFO 9 4) 30 /* LG MODIF */ CHGVAR %SST(&CHGINFO 13 30) + (&SRCF *CAT &SRCL *CAT &NEWNAME) CHGVAR %BIN(&ERRCOD) 0 CALL QLICOBJD PARM( + &RTNLIB + &NOMOBJ + &OBJTYP + &CHGINFO + &ERRCOD )
CPP de la commande CHGOBJAPI ce pgm permet de modifier certains attributs à priori inacessibles d'un objet:
+ coordonnées source , date de modif du source
+ produit (au sens logiciel sous licence) ayant créé cet objet
+ attribut utilisateur (10 c à votre disposition)
+ objet modifiable par API ? (celle-ci entre autre)
1/ Calcul le nombre d'infos ŕ modifier (&NBR) 2/ Concatčne les différentes modifs dans une seule variable sous la forme <-clé-><-lg-><-nouvelles valeurs-> repété x fois
PGM PARM(&OBJQ &OBJT &SRCELEM &SRCDATE + &COMPILELEM &PRDELEM &OPTION &ATTRIB + &COMPOS &ALWCHG)
DCL VAR(&OBJQ) TYPE(*CHAR) LEN(20) DCL VAR(&OBJT) TYPE(*CHAR) LEN(10) DCL VAR(&SRCELEM) TYPE(*CHAR) LEN(32) DCL VAR(&SRCDATE) TYPE(*CHAR) LEN(13) DCL VAR(&COMPILELEM) TYPE(*CHAR) LEN(15) DCL VAR(&PRDELEM) TYPE(*CHAR) LEN(15)
DCL VAR(&OPTION) TYPE(*CHAR) LEN(2) DCL VAR(&ATTRIB) TYPE(*CHAR) LEN(10) DCL VAR(&COMPOS) TYPE(*CHAR) LEN(10) DCL VAR(&ALWCHG) TYPE(*CHAR) LEN(1)
DCL VAR(&RTNLIB) TYPE(*CHAR) LEN(10) DCL VAR(&CODERR) TYPE(*CHAR) LEN(4) + VALUE(X'00000000') DCL VAR(&NBR) TYPE(*DEC) LEN(2 0) DCL VAR(&VAR2000) TYPE(*CHAR) LEN(2000) DCL VAR(&X41) TYPE(*CHAR) LEN(1) VALUE(X'41') DCL VAR(&DEP) TYPE(*DEC) LEN(4 0) VALUE(5) /* VARIABLES UTILISEES PAR LA GESTION DE MESSGAES */ DCL &ERRORSW *LGL /* SWITCH */ DCL &MSGID *CHAR LEN(7) /* ID MSG */ DCL &MSGDTA *CHAR LEN(100) /* DATA */ DCL &MSGF *CHAR LEN(10) /* FICHIER */ DCL &MSGFLIB *CHAR LEN(10) /* BIBLI */
MONMSG MSGID(CPF0000) EXEC(GOTO ERREUR)
/* CORPS DU PROGRAMME */
/* SOURCE*/ IF (%BIN(&SRCELEM 1 2) > 1) DO IF (%SST(&SRCELEM 3 5) ^= '*SAME') THEN(DO) CHGVAR &NBR (&NBR + 1) CHGVAR %BIN(&VAR2000 &DEP 4) 1 /* CLE 1 */ CHGVAR &DEP (&DEP + 4) CHGVAR %BIN(&VAR2000 &DEP 4) 30 /* LG INFOS */ CHGVAR &DEP (&DEP + 4) CHGVAR %SST(&VAR2000 &DEP 30) %SST(&SRCELEM 3 30) CHGVAR &DEP (&DEP + 30) ENDDO ENDDO
/* DATE */ IF (&SRCDATE ^= '*SAME') THEN(DO) CHGVAR &NBR (&NBR + 1) CHGVAR %BIN(&VAR2000 &DEP 4) 2 /* CLE 2 */ CHGVAR &DEP (&DEP + 4) CHGVAR %BIN(&VAR2000 &DEP 4) 13 /* LG INFOS */ CHGVAR &DEP (&DEP + 4) CHGVAR %SST(&VAR2000 &DEP 13) &SRCDATE CHGVAR &DEP (&DEP + 13) ENDDO
/* COMPIL*/ IF (%BIN(&COMPILELEM 1 2) > 0) DO IF (%SST(&COMPILELEM 3 5) ^= '*SAME') THEN(DO) CHGVAR &NBR (&NBR + 1) CHGVAR %BIN(&VAR2000 &DEP 4) 3 /* CLE 3 */ CHGVAR &DEP (&DEP + 4) CHGVAR %BIN(&VAR2000 &DEP 4) 13 /* LG INFOS */ CHGVAR &DEP (&DEP + 4) IF (%SST(&COMPILELEM 3 5) = '*BLANK') + CHGVAR %SST(&VAR2000 &DEP 13) ' ' ELSE + CHGVAR %SST(&VAR2000 &DEP 13) %SST(&COMPILELEM 3 13) CHGVAR &DEP (&DEP + 13) ENDDO ENDDO
/* PROD */ IF (%BIN(&PRDELEM 1 2) > 0) DO IF (%SST(&PRDELEM 3 5) ^= '*SAME') THEN(DO) CHGVAR &NBR (&NBR + 1) CHGVAR %BIN(&VAR2000 &DEP 4) 5 /* CLE 5 */ CHGVAR &DEP (&DEP + 4) CHGVAR %BIN(&VAR2000 &DEP 4) 13 /* LG INFOS */
CHGVAR &DEP (&DEP + 4) IF (%SST(&PRDELEM 3 5) = '*BLANK') + CHGVAR %SST(&VAR2000 &DEP 13) ' ' ELSE + CHGVAR %SST(&VAR2000 &DEP 13) %SST(&PRDELEM 3 13) CHGVAR &DEP (&DEP + 13) ENDDO ENDDO
/* OPTION*/ IF (%SST(&OPTION 1 5) ^= '*SAME') DO CHGVAR &NBR (&NBR + 1) CHGVAR %BIN(&VAR2000 &DEP 4) 13 /* CLE 13 */ CHGVAR &DEP (&DEP + 4) CHGVAR %BIN(&VAR2000 &DEP 4) 2 /* LG INFOS */ CHGVAR &DEP (&DEP + 4) CHGVAR %SST(&VAR2000 &DEP 2) &OPTION CHGVAR &DEP (&DEP + 2) ENDDO
/* ATTRIB*/ IF (%SST(&ATTRIB 1 5) ^= '*SAME') DO CHGVAR &NBR (&NBR + 1) CHGVAR %BIN(&VAR2000 &DEP 4) 9 /* CLE 9 */
CHGVAR &DEP (&DEP + 4) CHGVAR %BIN(&VAR2000 &DEP 4) 10 /* LG INFOS */ CHGVAR &DEP (&DEP + 4) CHGVAR %SST(&VAR2000 &DEP 10) &ATTRIB CHGVAR &DEP (&DEP + 10) ENDDO
/* COMPOS*/ IF (%SST(&COMPOS 1 5) ^= '*SAME') DO CHGVAR &NBR (&NBR + 1) CHGVAR %BIN(&VAR2000 &DEP 4) 14 /* CLE 14 */ CHGVAR &DEP (&DEP + 4) CHGVAR %BIN(&VAR2000 &DEP 4) 4 /* LG INFOS */ CHGVAR &DEP (&DEP + 4) CHGVAR %SST(&VAR2000 &DEP 4) &COMPOS CHGVAR &DEP (&DEP + 4) ENDDO
/* MODIF */ IF (&ALWCHG ^= '2') DO CHGVAR &NBR (&NBR + 1) CHGVAR %BIN(&VAR2000 &DEP 4) 8 /* CLE 8 */ CHGVAR &DEP (&DEP + 4) CHGVAR %BIN(&VAR2000 &DEP 4) 1 /* LG INFOS */
CHGVAR &DEP (&DEP + 4) CHGVAR %SST(&VAR2000 &DEP 1) &ALWCHG ENDDO
CHGVAR VAR(%BIN(&VAR2000 1 4)) VALUE(&NBR)
CALL QLICOBJD PARM( + &RTNLIB + &OBJQ + &OBJT + &VAR2000 + &CODERR)
/* RENVOI DES MESSAGES DE TYPE *COMP SI FIN NORMALE */
COMPMSG: RCVMSG MSGTYPE(*COMP) MSGDTA(&MSGDTA) MSGID(&MSGID) + MSGF(&MSGF) SNDMSGFLIB(&MSGFLIB) IF (&MSGID *EQ ' ') RETURN /* FIN DU PGM */ SNDPGMMSG MSGID(&MSGID) MSGF(&MSGFLIB/&MSGF) + MSGDTA(&MSGDTA) MSGTYPE(*COMP) GOTO COMPMSG /* BOUCLE SUR MESSAGES *COMP */
/*----------------------------------------*/ ERREUR: /* GESTION DES ERREURS */ /*----------------------------------------*/ IF &ERRORSW SNDPGMMSG MSGID(CPF9899) + MSGF(QCPFMSG) MSGTYPE(*ESCAPE) /* 2EME FOIS*/ /* ARRET PGM*/ CHGVAR &ERRORSW '1' /* MISE EN PLACE DU SWTICH */
/* RENVOI DES MESSAGES DE TYPE *DIAG SI FIN ANORMALE */ DIAGMSG: RCVMSG MSGTYPE(*DIAG) MSGDTA(&MSGDTA) MSGID(&MSGID) + MSGF(&MSGF) SNDMSGFLIB(&MSGFLIB) IF (&MSGID *EQ ' ') GOTO EXCPMSG SNDPGMMSG MSGID(&MSGID) MSGF(&MSGFLIB/&MSGF) + MSGDTA(&MSGDTA) MSGTYPE(*DIAG) GOTO DIAGMSG /* BOUCLE SUR MESSAGES *DIAG */
/* RENVOI DU MESSAGE D'ERREUR */ EXCPMSG: RCVMSG MSGTYPE(*EXCP) MSGDTA(&MSGDTA) MSGID(&MSGID) + MSGF(&MSGF) SNDMSGFLIB(&MSGFLIB) SNDPGMMSG MSGID(&MSGID) MSGF(&MSGFLIB/&MSGF) + MSGDTA(&MSGDTA) MSGTYPE(*ESCAPE) ENDPGM
deuxième exemple : QMHRTVM, retourne les caractèristiques d'un message dans un *MSGF. la clé du message doit être fournie. le paramètre OPTION permet de demander le message suivant.
* variable retour pour le format RTVM0300 Dmessage_rt DS 32767 D byte_retour 10I 0 D byte_dispo 10I 0 D msgid_rt 7 overlay(message_rt : 27) D msg_offset 10I 0 overlay(message_rt : 65) D msg_retour 10I 0 overlay(message_rt : *next) D msg_dispo 10I 0 overlay(message_rt : *next) D msg2_offset 10I 0 overlay(message_rt : 77) D msg2_retour 10I 0 overlay(message_rt : *next) D msg2_dispo 10I 0 overlay(message_rt : *next) D data 9999
d msg1 S 256 d msg2 S 9000
* divers paramètres Dmessage_rt_lg S 10I 0 inz(%size(message_rt)) Dformat S 8 inz('RTVM0300') dmsgid S 7 dmsgf S dmsgdta S 1 dmsgdta_lg S 10I 0 inz(0) dremplace_var S 10 inz('*NO') dremplace_spc S 10 inz('*NO')
Derror_code DS D err_lg 10I 0 inz(%size(error_code)) D err_retour 10I 0 D err_filler 1 D err_msgid 7
Doption S 10 inz('*FIRST') Dmsg_ccsid S 10I 0 inz(0) Ddta_ccsid S 10I 0 inz(0)
* prototype d'appel à l'API
Drtvmsg PR EXTPGM('QMHRTVM') D 9999 D 10I 0 D 8 d 7 d 20 d 1 d 10I 0 d 10 d 10 D 16 D 10 D 10I 0 D 10I 0 * on recoit msgf et bibliothèque en paramètre C *entry plist C parm p_msgf 10 C parm p_msgfl 10 /free msgf = p_msgf + p_msgfl; exsr lecture; option = '*NEXT';
dow byte_dispo > 0;
msg1 = %subst(message_rt : msg_offset + 1 : msg_retour);
if msg2_offset > 0; msg2 = %subst(message_rt : msg2_offset + 1 : msg2_retour); else; msg2 = *blanks; endif;
msgid = msgid_rt; // positionnement sur le message suivant exsr lecture; enddo; *inlr = *on;
// appel à l'API. begsr lecture; rtvmsg(message_rt : message_rt_lg : format : msgid : msgf : msgdta : msgdta_lg : remplace_var : remplace_spc : error_code : option : msg_ccsid : dta_ccsid); endsr; /end-free
Troisième cas,les API produisant une liste dans un USER SPACE.
Structure d'un USER SPACE.
De à .......................... -- 1 01: Espace utilisateur : ! Octets à blanc pour : (64 octets à blanc) : ! la communication ..........................64 -- entre programmes 2 65: ENTETE GENERALE : ! : DU USER SPACE : > Découpage image : : ! suivante ..........................140 -- 3 141: Rappel des paramètres : INPUT ! : reçus par l'API : ! .......................... ! 4 : En-tête API : HEADER ! voir structures : (valeurs rencontrées) : > dans QSYSINC .......................... ! (5769ss1 opt 13.) 5 : Liste générée par API : LIST ! : (liste des membres, : -- : des objets...) : :........................:???
....................................................................... : De : à :Fmt: Signification ( DECOUPAGE DE "ENTETE" ) : ......................................................................: : 65 : 68 : B : Taille de l'en-tête générale (2) : : 69 : 72 : C : Version : : 73 : 80 : C : Nom du format utilisé par l'API (ex:OBJD0100) : : 81 : 90 : C : Nom de l'API ayant généré la liste : : 91 : 103 : C : Siècle-date-heure(SDDDDDDHHHHHH) de remplissage : : 104 : 104 : C : Etat (C=Complet,I=Incomplet,P=Partiel) : : 105 : 108 : B : Nb d'octets utilisés dans le user space : : : : : : 109 : 112 : B : Déplacement pour atteindre la zone INPUT : : 113 : 116 : B : Taille de la zone INPUT : : : : : : 117 : 120 : B : Déplacement pour atteindre la zone HEADER : : 121 : 124 : B : Taille de la zone HEADER : : : : : : 125 : 128 : B : Déplacement pour atteindre la liste (LIST) : : 129 : 132 : B : Taille de la liste : : 133 : 136 : B : Nb d'entrées dans la liste : : 137 : 140 : B : Taille de chaque entrée. : .:.......:.......:...:.................................................:
* * Avec RPGIV en 3.70, la gestion des pointeurs pour lire un *USRSPC * est simplifiée, puisqu'il est possible d'écrire : * * C eval pt = pt + lgposte * * pt étant le pointeur , "lgposte" la variable en entête de User * Space, donnant la longueur d'un poste (137/140) * * lecture d'un user space contenant la liste des membres d'un fichier * ===================================================================
* va contenir l'adresse de début du User Space Dpointeur s * * l'entête (positions 125 à 140) * Dptrinfos s * DRTVINF ds based(ptrinfos) D offset 10i 0 D taille 10i 0 D nbpostes 10i 0 D lgposte 10i 0
* la liste des membres * dptrliste s * DLIST ds based(ptrliste) d ... (informations membre: nom, date de création etc...)
* prototype pour API qui retrouve pointeur de début dQUSPTRUS PR EXTPGM('QUSPTRUS') d space 20 const d ptr * /free
// extraction du pointeur de début (le user space doit exister) QUSPTRUS('EXEMPLE QTEMP' : pointeur);
// positionnement sur la partie entête ptrinfos = pointeur + 124 ;
// maintenant RTVINF (DS) a un contenu valide
// (rappel) //RTVINF e ds based(ptrinfos) // offset 10i 0 // taille 10i 0 // nbpostes 10i 0 // lgposte 10i 0
// positionnement sur le premier poste // (la structure "LIST" vient se positionner "par dessus")
ptrliste = pointeur + offset ;
// boucle (nbpostes fois) for i = 1 to nbpostes ; // traitement d'un élément ... ... ... if i < nbpostes ; ptrliste = ptrliste + lgposte; endif ; endfor ;
/end-free
Voilà, tout ceci est assez classique (au moins assez ancien [V2Rxx] ), si ce n'est l'utilisation des pointeurs et le format libre du GAP4.
Il reste une série d'API que nous n'avons pas du tout l'habitude d'utiliser ce sont les routines destinées au langage C.
Le C est un langage plutot pauvre, puissant et livré avec une bibliothèque d'outils assez étoffée. Ces routines sont externes et doivent être "linkées" au pgm principal [main()].
Sur AS/400, elles sont fournies sous la forme de programmes de service dans la cadre des techniques ILE. C'est a dire utilisables AUSSI en CLLE [ILE CL] ou en RPGLE [ILE GAP4].
Par exemple, utilisation des fonctions mathémathiques (source news/400):
* ========================================================= * = Using C function library trigonometric functions = H BndDir( 'QC2LE' ) H DftActGrp( *No ) D GetCosine Pr 8F ExtProc( 'cos' ) D 8F Value D GetArcCosine Pr 8F ExtProc( 'acos' ) D 8F Value D Pi 8F D Angle S 3 0 Inz( 30 ) D Radians S 8F D Cosine S 8F C Eval Pi = GetArcCosine( -1 ) C Eval Radians = ( Angle * Pi ) / 180 C Eval Cosine = GetCosine( Radians ) C Eval *InLR = *On |
dans ces programmes de service , vous trouverez tout ce qui permet de manipuler
des fichiers streams (dans IFS).
Démonstration, avec l'API stat() qui permet d'obtenir des informations générales
sur un fichier stream, s'il existe, donc aussi, de tester l'existence.
ce pgm CL (type de source CLLE) vérifie l'exitence d'un fichier IFS.
PGM PARM(&PARM)
DCL VAR(&PARM) TYPE(*CHAR) LEN(256) DCL VAR(&RTNVALINT) TYPE(*CHAR) LEN(4) DCL VAR(&RTNVAL) TYPE(*CHAR) LEN(2) DCL VAR(&PATH) TYPE(*CHAR) LEN(100) DCL VAR(&NULL) TYPE(*CHAR) LEN(1) VALUE(X'00') DCL VAR(&BUF) TYPE(*CHAR) LEN(4096)
CHGVAR VAR(&PATH) VALUE(&PARM *TCAT &NULL)
CALLPRC PRC('stat') PARM(&PATH &BUF) + RTNVAL(%BIN(&RTNVALINT 1 4))
CHGVAR VAR(&RTNVAL) VALUE(%BIN(&RTNVALINT))
IF COND(&RTNVAL *NE '00') THEN(SNDPGMMSG + MSGID(CPF9897) MSGF(QCPFMSG) + MSGDTA('Fichier ' !! &PARM !< ' non + trouvé.') MSGTYPE(*ESCAPE)) ENDPGM
même routine, mais utilisée en RPG (il s'agit ici de retrouver la taille)
* NOTE: Compiler avec DFTACTGRP(*NO) DFTACTGRP(QILE) BDNDIR('QC2LE') * * IFS API prototypes * * stat() * Dstat PR 10I 0 extproc('stat') Dpathptr * value Dstatptr * value * * variables de travail *
D statds DS D mode 10U 0 D ino 10U 0 D nlink 5U 0 D filler1 5U 0 D uid 10U 0 D gid 10U 0 D size 10U 0 D filler2 256
DFile_exists S 10I 0 Dpathptr S * Dpathname S 256 Dstatptr S * * * Main() * C *entry plist C parm filename 255 C parm taille 10 0 /free
//attribution d'un pointeur sur la variable nom du chemin pathname = %trim(filename)+x'00' ; pathptr = %addr(pathname) ; statptr = %addr(statds) ; // Appel de l'API IFS File_Exists = stat(pathptr:statptr) ;
if File_Exists = 0 ; taille = size ; endif ; // fin du pgm. *inlr = *on ; /end-free
et enfin, utilisation des routines open(), read(), write() etc...
le pgm qui suit est un extrait de l'exellent redbook : SG24-5402-00
Who Knew You Could Do That with RPG IV? A Sorcerers Guide to System Access and More [ www.redbooks.ibm.com ]
***************************************************************** * RPG PROGRAM EXAMPLE * This program is an example of using AS/400 Unix-style APIs * in an ILE-RPG program. * * LICENSE AND DISCLAIMER * ---------------------- * This material contains IBM copyrighted sample programming * source code. IBM grants you a nonexclusive license to use, * execute, display, reproduce, distribute and prepare derivative * works of this sample code. The sample code has not been * thoroughly tested under all conditions. IBM, therefore, does * not warrant or guarantee its reliability, serviceablity, or * function. All sample code contained herein is provided to you * "AS IS." ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT * NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILLITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. * * COPYRIGHT * --------- * (C) COPYRIGHT IBM CORP. 1997, 1998
* ALL RIGHTS RESERVED. * US GOVERNMENT USERS RESTRICTED RIGHTS - * USE, DUPLICATION OR DISCLOSURE RESTRICTED * BY GSA ADP SCHEDULE CONTRACT WITH IBM CORP. * LICENSED MATERIAL - PROPERTY OF IBM * * COMMENTS/QUESTIONS * ------------------ * Please send comments or questions via e-mail to Ray Bills * at rainfall@us.ibm.com also phone calls are welcome to Ray * at 507-253-4699 * * I would strongly suggest * that you remove all of the function prototypes into their * own RPG include file. That way, when IBM comes out with the * official version of the RPG include file, your code won't be * impacted that much. * * The program reads through a DDS-defined AS/400 Physical File * and creates an ASCII version of it. There is no attempt * to do any selection criteria, but that could be easily added * in your real versions. Also, the format of the lines of
* output can be easily changed to match the format of whatever * PC or Unix file you desire. * * Questions about this code are freely answered by ME * * * Please Note: I freely stole large protions of this code from * Massimo Marasco of IBM Italy, so if this turns * out to be successful and usefull, he should * get much of the credit. * *****************************************************************
***************************************************************** * * START OF WHAT SHOULD BE IN A RPG INCLUDE FILE * ***************************************************************** ***************************************************************** * * (from member STDIO, file H, library QSYSINC) *
* QBFC_EXTERN void perror(const char *); * ***************************************************************** * Dperror PR 10I 0 EXTPROC('perror') * string to be filled D * VALUE ***************************************************************** * * (from member STDIO, file H, library QSYSINC) * * QBFC_EXTERN int sprintf(const char *, const char *, ...); * ***************************************************************** * value returned = 0 (OK), -1 (error) Dsprintf PR 10I 0 EXTPROC('sprintf') * string to be filled D * VALUE * format string D * VALUE * D 10I 0 VALUE OPTIONS(*NOPASS)
* D * VALUE OPTIONS(*NOPASS) ***************************************************************** ***************************************************************** * * (from member FCNTL, file H, library QSYSINC) * * QBFC_EXTERN int open(const char *, int, ...); * ***************************************************************** * value returned = file descriptor (OK), -1 (error) Dopen PR 10I 0 EXTPROC('open') * path to be opened. D * VALUE * Open flags D 10I 0 VALUE * (OPTIONAL) mode (describes access rights to the file) D 10U 0 VALUE OPTIONS(*NOPASS) * (OPTIONAL) codepage (specified for ascii data) D 10U 0 VALUE OPTIONS(*NOPASS) ***************************************************************** *****************************************************************
* * (from member UNISTD, file H, library QSYSINC) * * QBFC_EXTERN ssize_t read(int, void *, size_t); * ***************************************************************** * value returned = number of bytes actually read, or -1 Dread PR 10I 0 EXTPROC('read') * file descriptor returned from open() D 10I 0 VALUE * data received D * VALUE * number of bytes to read D 10U 0 VALUE ***************************************************************** ***************************************************************** * * (from member UNISTD, file H, library QSYSINC) * * QBFC_EXTERN ssize_t write(int, const void *, size_t); * * Definition of type ssize_t
* (from member TYPES, file SYS, library QSYSINC) * * typedef int ssize_t; * * Definition of type size_t * (from member TYPES, file SYS, library QSYSINC) * * typedef unsigned int size_t; * ***************************************************************** * value returned = number of bytes actually written, or -1 Dwrite PR 10I 0 EXTPROC('write') * file descriptor returned from open() D 10I 0 VALUE * data to be written D * VALUE * number of bytes to write D 10U 0 VALUE ***************************************************************** ***************************************************************** * * (from member UNISTD, file H, library QSYSINC)
* * QBFC_EXTERN int close(int); * ***************************************************************** * value returned = 0 (OK), or -1 Dclose PR 10I 0 EXTPROC('close') * file descriptor returned from open() D 10I 0 VALUE ***************************************************************** ***************************************************************** ***************************************************************** ***************************************************************** * * END OF WHAT SHOULD BE IN A RPG INCLUDE FILE * ***************************************************************** * ***************************************************************** * RC is used to store the Return Code of the various IFS APIs ***************************************************************** DRC S 10I 0 *****************************************************************
* FileNam is the name of the file in the IFS namespace. You * can change this to be whatever you want. Your life will be * a lot easier if you make sure it starts with a slash '/'. ***************************************************************** DFileNam S 50A INZ('/junk.dat') DFileNamP S * INZ(%ADDR(FileNam)) ***************************************************************** * FileDescr is the File descriptor that gets assigned by the * open API and gets used for all of the read, write and close AP ***************************************************************** DFileDescr S 10I 0 ***************************************************************** * The following comments explain how the various numeric fields * were assigned for the open API. The values mentioned here are * from the FCNTL member of the H file in the QCLE library. ***************************************************************** * The following are for the 'oflag' parameter: * * define O_CREAT 0x0000008 /* Create the file if not there * define O_TRUNC 0x0000040 /* Clear file if it is there * define O_WRONLY 0x0000002 /* Open for writing only * define O_TEXTDATA 0x1000000 /* Translate ebcidic/ascii
* define O_CODEPAGE 0x0800000 /* Create file in ascii ccsid * ***************************************************************** DO_CREAT S 10I 0 INZ(8) DO_RDWR S 10I 0 INZ(4) DO_TEXTDATA S 10I 0 INZ(16777216) DO_CODEPAGE S 10I 0 INZ(8388608) Doflag S 10I 0 INZ(0) * ***************************************************************** * The mode parameter for the open API is set to give acces * all users. 0x01B6 = rw-w-w-data rights *********************************************************** Domode S 10U 0 INZ(438) * *********************************************************** * cp is used to set the code page (CCSID) of the IFS file * a common US English ASCII. Others code be substituted a * desired. *********************************************************** * ASCII (ccsid 437 = 0x1B5) Dcp S 10U 0 INZ(819)
* *********************************************************** * The following fields are used to help fo the string * formatting and writing... ***************************************************************** * DZeroBin S 50A INZ(*ALLX'00') DNLZero S 2A INZ(X'1500') * ***************************************************************** * SI_Fmt is used to hold the format that you want to put in your * ascii file. This follows the format for the C function called * printf. So if you are unfamiliar with it, check out a C book * for further details. The quick tutorial is as follows: * %d means put a SIGNED number here. (ex. -123 or 456) * , the comma is used to delimit the fields * %s means to put a string or name here. ***************************************************************** * DSI_Fmt S 50A INZ('%d, %s') DSI_FmtP S * INZ(%ADDR(SI_Fmt)) *
***************************************************************** * SI_Msg is used to hold the string or name data from the DB fil * I have put the phrase "Hello World" it there just for fun. ***************************************************************** * DSI_Msg S 50A INZ('Hello World') DSI_MsgP S * INZ(%ADDR(SI_Msg)) * ***************************************************************** * num is where I will put some numeric data. Note that the EVAL * statement below will take care of unpacking the data from the * DB file. The SI_Fmt above takes care of putting the '-' sign * in the right place automatically for us!! ***************************************************************** * Dnum_ds DS D num_Hex 4A INZ(X'00000000') D num 10I 0 OVERLAY(num_Hex) * ***************************************************************** * Buf is the place where we build our string that will go into t * ascii file for us. It needs to be big enough to hold all of
* the data for one record of output (including formatting). ***************************************************************** * DBuf S 100A DBufP S * INZ(%ADDR(Buf)) DBufLen S 10U 0 INZ(100) * ***************************************************************** * Here we start the logic. * * 1. Use the open API to create the file, specifying that the da * to be stored in it will be in codepage (ccsid) 437 (or whateve * you change it to above in the CP field). * ***************************************************************** * C EVAL FileNam=%TRIM(FileNam) + ZeroBin C Z-ADD O_CREAT oflag C ADD O_RDWR oflag C ADD O_CODEPAGE oflag C EVAL FileDescr=open(FileNamP:oflag:omode:cp) C IF FileDescr=-1
C EVAL RC = perror(FileNamP) C return C ENDIF ***************************************************************** * * 2. Use the close API to close the file. This may seem strange * since we are going to turn right around and reopen the file, * but that is so it will do the automatic translation for us fro * our current job CCSID (whatever it happens to be) into the * ascii CP. ***************************************************************** * C EVAL RC=close(FileDescr) * C IF RC=-1 C EVAL RC = perror(FileNamP) C return C ENDIF * ***************************************************************** * * 3. Use the open API to reopen the file with NEW oflag values.
* These will handle the ebcidic to ascii translation for us. ***************************************************************** C Z-ADD O_RDWR oflag C ADD O_TEXTDATA oflag C EVAL FileDescr=open(FileNamP:oflag) * C IF FileDescr=-1 C EVAL RC = perror(FileNamP) C return C ENDIF ***************************************************************** * * 4.Use the write API to put translate the data from ebcidi * to ascii and store it in the IFS file. ***************************************************************** * C EVAL buf='Hello Ray' + X'15' C EVAL RC=write(FileDescr: BufP: BufLen)
C return C ENDIF
* ***************************************************************** * * 5. Use the close API to close the IFS file. * ***************************************************************** ***************************************************************** C EVAL RC=close(FileDescr) C IF RC=-1 C EVAL RC = perror(FileNamP) C return C ENDIF * C SETON Lr
ce source est assez complet (peut-être trop car il reprend des déclarations un peu inutiles, à titre d'exemple)
vous noterez l'ouverture du fichier une première fois pour création. puis une deuxième fois pour la conversion :le système transforme vos data en code-page du fichier [ascii], uniquement sur un fichier existant.
Pour terminer un exemple récapitulatif, génération d'un fichier CSV à partir d'un *MSGF :
H DFTACTGRP(*no) ACTGRP('QILE') BNDDIR('QC2LE') D***************************************************************** D* PROTOTYPES IFS D***************************************************************** D*** open sur IFS Dopen PR 10I 0 EXTPROC('open') D nomfichier * VALUE D openflags 10I 0 VALUE D mode 10U 0 VALUE OPTIONS(*NOPASS) D codepage 10U 0 VALUE OPTIONS(*NOPASS) D*** lecture IFS Dread PR 10I 0 EXTPROC('read') D filehandle 10I 0 VALUE D datareceived * VALUE D nbytes 10U 0 VALUE D*** écriture IFS Dwrite PR 10I 0 EXTPROC('write') D filehandle 10I 0 VALUE D datatowrite * VALUE D nbytes 10U 0 VALUE D*** fermeture IFS Dclose PR 10I 0 EXTPROC('close')
D filehandle 10I 0 VALUE D***************************************************************** D* Constantes pour IFS D*** File Access Modes for open() D O_RDONLY S 10I 0 INZ(1) D O_WRONLY S 10I 0 INZ(2) D O_RDWR S 10I 0 INZ(4) D*** oflag Values for open() D O_CREAT S 10I 0 INZ(8) D O_EXCL S 10I 0 INZ(16) D O_TRUNC S 10I 0 INZ(64) D*** File Status Flags for open() and fcntl() D O_NONBLOCK S 10I 0 INZ(128) D O_APPEND S 10I 0 INZ(256) D*** oflag Share Mode Values for open() D O_SHARE_NONE S 10I 0 INZ(2000000) D O_SHARE_RDONLY S 10I 0 INZ(0200000) D O_SHARE_RDWR S 10I 0 INZ(1000000) D O_SHARE_WRONLY S 10I 0 INZ(0400000) D*** file permissions D S_IRUSR S 10I 0 INZ(256) D S_IWUSR S 10I 0 INZ(128)
D S_IXUSR S 10I 0 INZ(64) D S_IRWXU S 10I 0 INZ(448) D S_IRGRP S 10I 0 INZ(32) D S_IWGRP S 10I 0 INZ(16) D S_IXGRP S 10I 0 INZ(8) D S_IRWXG S 10I 0 INZ(56) D S_IROTH S 10I 0 INZ(4) D S_IWOTH S 10I 0 INZ(2) D S_IXOTH S 10I 0 INZ(1) D S_IRWXO S 10I 0 INZ(7) D*** misc D O_TEXTDATA S 10I 0 INZ(16777216) D O_CODEPAGE S 10I 0 INZ(8388608) D***************************************************************** D* DEFINITIONS D***************************************************************** D*** divers D nomfichier S 255A D lgfichier S 10I 0 D AsciiCodePage S 10U 0 INZ(1252) D FullName S 512A D ReturnInt S 10I 0
D*** D File_Hdl S 10I 0 D Byteswrt S 10I 0 D EOR S 2A Inz(X'0D25') D Null S 1A Inz(X'00') D ligne S 32767 ********************************************************************** * variable retour pour le format RTVM0300 Dmessage_rt DS 32767 D byte_retour 10I 0 D byte_dispo 10I 0 D msgid_rt 7 overlay(message_rt : 27) D msg_offset 10I 0 overlay(message_rt : 65) D msg_retour 10I 0 overlay(message_rt : *next) D msg_dispo 10I 0 overlay(message_rt : *next) D msg2_offset 10I 0 overlay(message_rt : 77) D msg2_retour 10I 0 overlay(message_rt : *next) D msg2_dispo 10I 0 overlay(message_rt : *next) D data 9999
d msg1 S 256 d msg2 S 9000
* divers paramètres Dmessage_rt_lg S 10I 0 inz(%size(message_rt)) Dformat S 8 inz('RTVM0300') dmsgid S 7 dmsgf S 20 inz('QCPFMSG QSYS') dmsgdta S 1 dmsgdta_lg S 10I 0 inz(0) dremplace_var S 10 inz('*NO') dremplace_spc S 10 inz('*NO')
Derror_code DS D err_lg 10I 0 inz(%size(error_code)) D err_retour 10I 0 D err_filler 1 D err_msgid 7
Doption S 10 inz('*FIRST') Dmsg_ccsid S 10I 0 inz(0) Ddta_ccsid S 10I 0 inz(0)
* prototype d'appel à l'API
Drtvmsg PR EXTPGM('QMHRTVM') D 9999 D 10I 0 D 8 d 7 d 20 d 1 d 10I 0 d 10 d 10 D 16 D 10 D 10I 0 D 10I 0 * on recoit msgf et bibliothèque en paramètre C *entry plist C parm p_msgf 10 C parm p_msgfl 10 C parm p_nomfic 255 /free msgf = p_msgf + p_msgfl; nomfichier = p_nomfic ;
lgfichier = %Len(%Trimr(nomfichier)) ; // Ouverture du fichier FullName = %TRIMR(nomfichier) + Null; file_Hdl = open(%ADDR(FullName) : O_CREAT + O_WRONLY + O_TRUNC + O_CODEPAGE : S_IRWXU + S_IROTH : AsciiCodePage); ReturnInt = close(file_Hdl); // 2ème ouverture file_Hdl = open(%ADDR(FullName) : O_TEXTDATA + O_RDWR); exsr lecture; option = '*NEXT'; dow byte_dispo > 0;
msg1 = %subst(message_rt : msg_offset + 1 : msg_retour); ligne = '"' + msgid_rt + '";"' + %trim(msg1) + '"';
if msg2_offset > 0; msg2 = %subst(message_rt : msg2_offset + 1 : msg2_retour); ligne = %trimr(ligne) + ';"' + %trim(msg2) + '"'; endif;
//Ecriture IFS ligne = %Trimr(ligne) + EOR; byteswrt = write(file_Hdl : %ADDR(ligne) : %LEN(%TRIMR(ligne))) ; // positionnement sur le message suivant msgid = msgid_rt; exsr lecture; enddo; ReturnInt = close(file_Hdl); *inlr = *on;
// S-PGM
// appel à l'API. begsr lecture; rtvmsg(message_rt : message_rt_lg : format : msgid : msgf : msgdta : msgdta_lg : remplace_var : remplace_spc : error_code : option : msg_ccsid : dta_ccsid); endsr; /end-free
Retourner au Début