, Access Client Solutions 1.1.8.1

La toute dernière version de ACS disponible depuis novembre 2018 apporte son lot d’améliorations. Sans surprise, ce sont les fonctionnalités liées à SQL qui évoluent le plus.

Petite revue des principales évolutions.

 

Script SQL

Les SELECT avec l’option FOR UPDATE permettent une modification directement dans l’affichage du résultat :

Possibilité de demander l’affichage de l’entête de colonne, soit dans les options, soit par click droit sur l’entête dans le résultat :

Schéma de base de données

Le choix de l’ASP est désormais possible :

Des améliorations dans différents affichages (objets associés), et la capacité à modifier les Alias, Masque de colonne et Droits sur une ligne.

 

Autres

Dans le centre de performances SQL, possibilité de filtrer les moniteurs, les images instantanées de cache, et moniteurs d’événements de cache :

Dans les sessions 5250, la bufferisation du clavier est désactivable :

Les sessions 5250 Client Access configurées avec Kerberos sont prises en charge dans l’outil de migration.

 

Pour les transferts de fichier :

  • le nom des onglets excel est personnalisable
  • Correction d’un bug sur les transferts de type .dsr (Dos Random) supportent l’UTF-8 => ATTENTION : cela produit une incompatibilité avec les versions précédentes d’ACS.

 

 

La version recommandée de Java est désormais Java 8 update 191 ou supérieur. Le script d’installation Windows vérifie la compatibilité avec la version de Java disponible sur le système.

 

La liste complète des dernières modifications : ftp://ftp.software.ibm.com/as400/products/clientaccess/solutions/readmespacs.txt

Toujours utile, les ressoucres ACS en ligne : https://www.ibm.com/developerworks/community/wikis/home?lang=en#!/wiki/IBM%20i%20Technology%20Updates/page/IBM%20i%20Access%20Resources

 

Vous êtes de plus en plus nombreux à utiliser IWS pour exposer des programmes (de service) ILE sous forme de service web ! Cette technique fonctionne bien : elle est simple, automatique, le code est écrit en RPG.

Mais utilisez vous toutes les possibilités fournies : XML ou JSON pour encapsuler les paramètres en sortie ?

De plus, nous sommes limités par le nombre de paramètres du code RPG :

  •  7 paramètres pour une procédure (SIC !!)
  • 256 paramètres pour un programme

 

Personnalisez vos services

Nous vous proposons aujourd’hui d’utiliser un type de media personnalisé en valeur de retour : vos services pourront retourner des documents de type XML, JSON, CSV, HTML, … en réalité tous les types usuels du web basés sur du texte.

 

Par exemple, la procédure suivante ramène les profils utilisateurs non système (la première lettre est différente de Q) et désactivés (une occasion de faire du ménage) :

Les paramètres :

  • data_LENGTH et data
    • longueur et valeur des données en sortie
  • httpStatus
    • permet le contrôle du code HTTP en retour par le programme RPG
    • un code 200 signifie OK et est celui généré automatiquement par l’outil. Un autre code connu : 404 NOT FOUND

 

Au déploiement du service vous devez indiquer certaines options :

On remarque :

  • HTTP response code output parameter -> httpStatus
  • Returned output media types -> application/xml

Cette dernière option signifie que la valeur de retour sera toujours un document XML

 

Exemple lors de l’appel depuis un navigateur :

Ici le navigateur se base sur le type indiqué pour le retour (application/xml) pour mettre en forme le résultat.

 

Encore plus !

Nous pouvons maintenant aller plus loin en rendant dynamique les types de données en sortie !

Par exemple, une procédure permettant de lire un fichier sur l’IFS dont le nom est transmis en paramètre, et de retourner son contenu :

Le nom du fichier à retourner est reçu dans le paramètre file.

Nous gérons dans le tableau httpHeaders() la valeur du type de contenu en retour via une entête HTTP Content-Type. Les valeurs usuelles sont listées ici : https://en.wikipedia.org/wiki/Media_type

Le type de contenu est défini en fonction de l’extension du fichier : application/xml pour tous les fichiers *.xml.

Si une extension n’est pas connu du programme, nous provoquons une erreur HTTP 406 NOT ACCEPTABLE.

De plus, si le fichier n’est pas trouvé, un code HTTP 404 NOT FOUND est généré.

 

Lors du déploiement, il est également nécessaire d’indiquer certaines options :

Principalement dans « Returned output media types » -> doit contenir la liste de tous les types de médias retournées par le programme. Une valeur qui ne serait pas présente ici sera interceptée par le serveur IWS et provoquera une erreur HTTP 500 (Internal Server Error) et aucune valeur ne sera retournée.

Les différentes valeurs sont séparées par des virgules :  application/xml, text/csv, text/html, text/plain.

Le swagger généré tient compte de ces informations pour une meilleur documentation et interopérabilité de vos services.

 

Lors des différents appels (les fichiers indiqués sur l’URL existent dans le répertoire /home/NB/ressources) :

 

Pour la gestion des cas d’erreur (extension non gérée ou fichier inexistant) :

 

Grâce à IWS, vos programmes RPG peuvent désormais facilement renvoyer tous types de documents, que le contenu soit statique (comme un fichier dans l’IFS) ou dynamique (calculé en live par votre programme). Et n’oubliez pas, DB2 offre de nombreuses possibilités (XML, JSON, accès à l’IFS …) qui sont donc directement utilisables en SQLRPGLE.

Prochaine étape : des fichiers binaires (image, pdf) !

Vous voulez faire un point sur vos opérations de sauvegardes, voici quelques points à regarder  ?

1) Vérifiez vos niveaux logiciels :

Assurez-vous d’avoir un bon niveau de PTF pour le groupe SF99724

Vous ne devez pas avoir plus de 6 mois de retard , vérifier sur le site IBM FIX CENTRAL

2) Vérifiez vos supports :

Assurez-vous que vos bandes ne sont pas trop vieilles, trois ans c’est le maximum pour du LTO.

Ça peut dégrader les performances, mais aussi et surtout, rendre impossible partiellement une restauration.

Vous pouvez avoir des informations sur vos supports dans le menu SST :

9. Work with save storage and restore storage

Ou en faisant un PRTERRLOG

Exemple :

PRTERRLOG TYPE(*VOLSTAT) VOLTYPE(3580)

Title . . . . . . . . . . : Volume Statistics Report
System type . . . . . . . : 8286
System model . . . . . . : 41A
System release . . . . . : V7R3M0
System name . . . . . . . : NEPTUNE
System serial number . . : 21-7D81V
Report type . . . . . . . : Lifetime Report
Removable media . . . . . : Self-configured tape and 1/4 inch cartridge tape
Volume ---Temporary Errors--- --------M Bytes--------
ID Read Write Read Written
GAIA 0 0 1 363822
JEUDI 0 0 17209 57078599
LUNDI 0 0 21917 30098797
MARDI 0 0 4153 29205718
MRCRDI 0 0 30 29331213

>>indicates media replacement is recommended
> indicates media replacement criteria should be checked

Remarque: l’option de cette commande ne peut être mise en base de données.

3) Faites le ménage :

Attention à tous les objets obsolètes, les duplications, les SAVF.

J’ai fait enlever 27 gigas chez un client …

Et surtout attention à l’IFS ou la gestion est souvent anarchique .

Pour vous aider vous pouvez utiliser des outils comme RTVDSKINF, pour suivre l’évolution de vos espaces

4) Vérifiez vos scripts :

Assurez-vous que vos commandes d’initialisation utilisent le maximum de compactage de vos supports, on trouve souvent des commandes qui date du dérouleur précédent

Le meilleur compromis est pour l’initialisation

==> INZTAP … DENSITY(*DEVTYPE)

et pour les commandes de sauvegarde

==> SAVLIB COMPACT(*DEV)

Supprimez les dlyjob notamment sur les arrêts de TCP/IP qui ne servent plus, 300 secondes c’est 10 minutes , j’ai vu 30 minutes de DLY dans un script CL.

Le temps de sauvegarde de l’ifs prend de plus en plus de temps notamment si vous avez beaucoup de petits fichiers.

Une option apparue par PTF en 6.1 et disponible depuis les autres versions permet de diminuer ce temps :

==> SAV … ASYNCBRING(*YES)

Le plus simple pour tester, modifier la valeur par défaut de la commande SAV

==> CHGCMDDFT CMD(SAV) NEWDFT(‘ASYNCBRING(*YES)’)

Prenez les temps avant et après, vérifiez le gain…

S’il n’y a pas de gain, probablement que votre machine n’a pas de mémoire, vous pouvez faire retours en arrière.

==> CHGCMDDFT CMD(SAV) NEWDFT(‘ASYNCBRING(*NO)’)

Sauvegardez les droits privés :

==> SAVLIB … PVTAUT(*YES)

Cela évitera une opération de RSTAUT.

5) Testez vos sauvegardes :

Par exemple, restaurez un objet en fin de bande, ce qui vous garantira l’intégrité complète de votre sauvegarde.

6) A ne pas faire :

Pour gagner du temps en sauvegarde, ne pas sauvegarder les indexes.

Le gain de temps sera largement perdu à la restauration.

7) A étudier

La sauvegarde des spools

==>SAVLIB SPLFDTA(*all)

Si vous n’avez pas besoin de vos spools ne les sauvegardez pas

Si vous utilisez cette option, assurez-vous de ne pas avoir de spool obsolètes , faites une épuration avant vos sauvegardes, J’ai vu 237000 spools sauvegardés inutilement…

Si vous devez utiliser du cryptage, choisissez du cryptage matériel fourni en standard sur les LTO.

Sauvegarde en While Active

Allonge considérablement les temps de sauvegarde, ne doit être utilisé qu’en cas d’arrêt impossible de la production

Découpez les sauvegardes

Attention à bien maitriser l’ensembles des supports et la procédure pour les restaurations

8 ) Conclusion

Il est possible qu’en ayant étudié tous ces points, vous ne puissiez pas optimiser mieux vos phases d’immobilisations, c’est que votre système est bien géré ou que vous allez devoir en changer !

Le but n’est pas ici de développer toutes les spécificités de la GDPR, par exemple, pour la gestion de vos registres de traitement vous n’échapperez pas à l’analyse de votre SI et sans doute vous aurez besoin d’une aide juridique si vous gérez des données sensibles ou confidentielles.

Mais l’idée est de voir comment votre IBM i peut vous aider techniquement à réaliser les obligations de la nouvelle loi.

Voici une liste de points à vérifier .

1) Identifier les personnes (pour que quelqu’un puisse faire quelque chose il lui faut un profil et un mot de passe …).

3 conseils :

  • Passez en mot de passe long.
    Passez vos accès SSH (pour que les MDP ne circulent pas en clair)
    Appliquez à votre IBM i la même politique de changement de Password que dans le reste votre entreprise, en attendant un single sinon.

2) Ajuster les droits des utilisateurs, en théorie une personne ne doit pas avoir plus de droits que ce que lui impose ses tâches .

3 conseils :

  • Analysez les profils qui ont le droit spécial *ALLOBJ.
    Analysez les droits sur les ressources avec authority collection
    Analysez les partages de L’IFS.
    Et après mettez en place une politique plus restrictive, attention aux effets de bord

3) Pour la tracabilité, les journaux d’audit propose un grand nombre d’informations

4 conseils :

  • Analysez les logs que vous collectez et gardez uniquement la partie pertinente
    Ajoutez des triggers after sur certaines tables pour loguer *
    Ajoutez des programmes d’exit sur les traitements (ODBC, FTP, PARTAGE, SQL)
    En cas de violation agissez …

* Ce point peut être mis en place à l’aide des tables temporelles, bien meilleur, mais plus compliqué à mettre en place. Par contre pour vos nouveaux traitements, pensez y !

4) Anonymisation et cryptage des données, vous pouvez crypter vos données dans l’ASP, dans vos sauvegardes par BRMS, et par SQL sur vos bases de données RCAC et Field Proc.

 

3 Conseils :

  • Sécurisez tous les échanges avec vos serveurs externes (avec certificat par exemple)
    Attention à la gestion de la pseudonimisation pour les data Whare house.
    Testez vos restaurations !

5) Registre de traitements.

Il vous faudra avoir une base de donnée centralisée, elle peut être sur votre ibm i …

Sauvegarde et Restauration des webservers

Pour Sauvegarder et Restaurer un server de webservices vous pouvez utiliser les scriptes QSH prévus à cet effet.

Sauvegarde

saveWebServicesServer.sh
-server ‘server’
-saveFile ‘savf’ (‘/qsys.lib/libsavf.lib/savf.file’)

Restauration

restoreWebServicesServer.sh
-fromServerDirectory ‘server-directory’ (‘/www/server’)
-saveFile ‘savf’ (‘/qsys.lib/libsavf.lib/savf.file’)

Remarque :

Le seul support est le SAVF

Mais comment restaurer un serveur de webservices, si vous n’avez que les répertoires de l’IFS.

Il faut savoir qu’un webserveur de webservices c’est 3 choses :

– des répertoires de configuration (dans /www/)
– une instance APACHE déclarée dans http server (fichier QATMHINSTC qui contient un membre pour chacun instance)
– un enregistrement dans LWI ou Liberty.

Il vous faudra donc respecter les étapes suivantes

1) Restauration des répertoires de L’IFS

  • RST DEV(‘/qsys.lib/qgpl.lib/savfIFS.file’)
    OBJ((‘/www/YOURSERVER’))
    Vous pouvez restaurer l’IFS à partir d’autres supports…

2) Autorisation de la branche du server au profil QTMHHTTP

Le profil QTMHHTTP est le profil utilisé par http server pour la plupart de ses opérations. Il doit avoir un droit explicite sur ces fichiers.

  • CHGAUT OBJ(‘/www/YOURSERVER/*’)
    USER(QTMHHTTP)
    DTAAUT(*RWX)
    OBJAUT(*ALL)
    SUBTREE(*ALL)

Remarque :

Cette étape peut être facultative si vous avez une sauvegarde avec les droits privées paramètres PVTAUT(*YES) de vos commandes SAVXXX.

Il vous faudra préciser dans la commande RST … le paramètre PVTAUT(*YES)

3) Création du membre de l’instance APACHE dans le fichier QATMHINSTC de QUSRSYS.

En utilisant la commande

wrkmbrpdm qusrsys/QATMHINSTC

Dupliquer un membre existant en lui donnant le nom de votre server YOURSERVER
puis modifier le contenu en lui indiquant le nom de votre serveur
-apache -d /www/YOURSERVER -f conf/httpd.conf

Cette modification peut-être faite, via l’option 18, par SQL, par script…

 

4) Enregistrement de votre serveur dans liberty ou LWI

Sous LWI

java -cp /QIBM/ProdData/OS400/jt400/lib/jt400Native.jar:/QIBM/ProdData/OS/OSGi/LWI81/native/iasadmin.jar com.ibm.lwi.admin.IntegratedServerAdmin -registerServer YOURSERVER /www

Remarque :
La version de votre lwi ici 8.1, dépend de votre système pour la connaitre :
Se mettre sous qsh
cd /QIBM/ProdData/OS/OSGi
puis ls
o


par exemple ici la version est 6.0
donc la commande devient
java -cp /QIBM/ProdData/OS400/jt400/lib/jt400Native.jar:/QIBM/ProdData/OS/OSGi/LWI60/native/iasadmin.jar com.ibm.lwi.admin.IntegratedServerAdmin -registerServer YOURSERVER /www

Sous Liberty

java -cp /QIBM/ProdData/OS400/jt400/lib/jt400Native.jar:/QIBM/ProdData/OS/OSGi/shared/lib/iasadmin.jar com.ibm.lwi.admin.IntegratedServerAdmin -registerServer YOURSERVER /www

Vous avez un fichier de propriétés, il est dans le répertoire LWI correspondant :

Votre serveur est opérationnel et vous pouvez l’utiliser à nouveau.

Les fichiers de référence des serveurs enregistrés se trouvent :
En 6.1, dans /QIBM/UserData/OS/OSGi/registry/registry.data pour Lwi

À partir de la 7.1,  dans /QIBM/UserData/OS/OSGi/registry/iasregistry.data pour Liberty

Vous pouvez désormais arrêter et redémarrer votre serveur.
ENDTCPSVR *HTTP HTTPSVR(YOURSERVER)
STRTCPSVR *HTTP HTTPSVR(YOURSERVER)

Exemple de requêtes SQL pour retrouver des informations

Liste des instances apaches démarrées

SELECT DISTINCT SERVER_TYPE
FROM TABLE(QSYS2.ACTIVE_JOB_INFO(
SUBSYSTEM_LIST_FILTER => ‘QHTTPSVR’)) X_table

WHERE function = ‘QZSRHTTP’

Liste des instances HTTP déclarées

SELECT TABLE_PARTITION as MEMBER FROM SYSPARTITIONSTAT
WHERE TABLE_NAME = ‘QATMHINSTC’ AND TABLE_SCHEMA = ‘QUSRSYS’

Remarque :

Admin n’a pas d’instance dans ce fichier

La jonction entre les tables pour voir celles qui ne sont pas démarrées

SELECT DISTINCT SERVER_TYPE, TABLE_PARTITION
FROM SYSPARTITIONSTAT a
Join TABLE(QSYS2.ACTIVE_JOB_INFO(
SUBSYSTEM_LIST_FILTER => ‘QHTTPSVR’)) B
on B.server_type = ‘QIBM_HTTP_’ concat TABLE_PARTITION
and TABLE_NAME = ‘QATMHINSTC’
AND TABLE_SCHEMA = ‘QUSRSYS’
where function = ‘QZSRHTTP’

Nous avons vu dans le premier article sur le PCML, que la gestion de l’usage des paramètres ne peut se faire dans le source d’un programme. On peut au mieux déclarer les paramètres en entrée, par le mot clé « const ».

Tous les autres paramètres seront déclarés en entrée/sortie dans le PCML.

Lors de déploiements de web service par script, il faut pouvoir gérer ces paramètres, de façon à générer des WSDL (SOAP) ou des SWAGGER (REST) corrects.

Cet article recense plusieurs méthodes possibles pour cette gestion.

PCML généré dans l’IFS – ajustement manuel :

Prérequis : génération du PCML dans l’IFS, options de compilation PGMINFO et INFOSTMF à renseigner.

On peut éditer le fichier et le modifier manuellement.
Il faudra faire attention à bien respecter le formalisme du PCML.

Avantages :

  •  Mise en œuvre minimaliste.

Inconvénients :

  • Il faut penser après génération du PCML à le modifier, risque d’oubli comme pour toutes procédures peu usitées.
  • Risque d’écraser le PCML modifié par le PCML standard lors d’une prochaine compilation.
  • Risque d’erreur dans la modification, rendant inopérant le PCML.
  • Ce n’est pas sport !

Cette méthode peut être utilisée si vous n’exposez que peu de web service, contenant que quelques paramètres. Même si nous la déconseillons fortement.

PCML généré dans l’IFS – ajustement programmatique :

Prérequis : génération du PCML dans l’IFS, options de compilation PGMINFO et INFOSTMF à renseigner.

Plutôt que d’éditer un PCML dans l’IFS, on peut prévoir de le modifier par programme, en se basant sur un formalisme de nommage des paramètres.
Par exemple, en suffixant le paramètre en entrée par « _I » et les paramètres en sortie par « _O ». Les paramètres en entrée/sortie n’auront pas besoin de suffixe, leurs usages dans le PCML est déjà correct.
Le programme devra mettre à jour le PCML dans l’IFS, en transformant l’usage des paramètres selon le suffixe.
Pour exemple, dans l’extrait de code ci-dessous, nous utilisons les API « C » pour modifier le PCML :
Si vous le souhaitez, nous pouvons sur demande fournir le code complet.

// Ouverture du fichier en mise à jour
 fd = OpenFile(Path : O_RDWR + O_TEXTDATA + O_CCSID : S_IRGRP : 37 ) ;
 if (fd < 0) ;
 // fichier non ouvert : non trouvé ou vérouillé ou ...
 // gestion personnalisée à faire
 *inlr = *on ;
 return ;
 endif ;

// Boucle de lecture infinie - sortie explicite via le leave
 // lecture du fichier totale, avec recherche de chaîne
 Pos = 0 ;
 dow (1 = 1) ;
 // récupération de la longueur du fichier entre la position actuelle
 // et la fin de fichier au premier passage, longueur totale du PCML
 Length = ReadFile(fd:%addr(Data):%size(Data)) ;

// Longueur à 0 --> fin de fichier on sort de la boucle
 if (Length = 0);
 leave;
 else;
 // Recherche du tag de déclaration d'un paramètre
 // on récupère sa position
 Debut = %scan('<data name="' : Data) ;
 if Debut > 0;
 // Extraction de la position du suffixe
 // En fin de nom paramètre sur 2
 Debut += 12 ; // longueur chaîne '<data name="'
 DebSuf = %scan('"' : Data : Debut) - 2;

// Extraction de la position de l'usage du paramètre
 DebUsage = %scan('usage="' : Data : Debut) + 6; // + lng'usage="'

// détermination de l'usage en fonction du suffixe
 select;
 when %scan('_I' : Data : DebSuf : 2) > 0;
 UsageIO = 'input" />' ;
 when %scan('_O' : Data : DebSuf : 2) > 0;
 UsageIO = 'output" />' ;
 other;
 UsageIO = 'inputoutput" />' ;
 endsl;

// positionnement sur l'adresse de début de modification
 Pos += DebUsage;
 Data = UsageIO ;
 lseek(fd: Pos: SEEK_SET) ;
 // modification du fichier
 WriteOK = write(fd:%addr(data): 15) ;
 Pos += 15 ;
 endif;
 endif;

enddo;

PCML issu de la compilation :

<pcml version="6.0"> 
 <!-- RPG program: TESTWS --> 
 <!-- created: 2018-03-12-11.09.56 --> 
 <!-- source: DT/QRPGLESRC(TESTWS) --> 
 <!-- 3 --> 
 <program name="TESTWS" path="/QSYS.LIB/DT.LIB/TESTWS.PGM"> 
 <data name="VARIN_I" type="char" length="15" usage="inputoutput" /> 
 <data name="VAROUT_O" type="char" length="30" usage="inputoutput" /> 
 </program> 
</pcml>

PCML après traitement par le PGM :

<pcml version="6.0"> 
 <!-- RPG program: TESTWS --> 
 <!-- created: 2018-03-12-11.09.56 --> 
 <!-- source: DT/QRPGLESRC(TESTWS) --> 
 <!-- 3 --> 
 <program name="TESTWS" path="/QSYS.LIB/DT.LIB/TESTWS.PGM"> 
 <data name="VARIN_I" type="char" length="15" usage="input" /> 
 <data name="VAROUT_O" type="char" length="30" usage="output" /> 
 </program> 
</pcml>

Avantages :

  •  Automatisation possible, à la création du web service, en masse sur un répertoire de l’IFS, … Au gré des besoins !
  • Si vous avez l’utilité du PCML pour d’autres applications que l’exposition des web services, vous pouvez par ce biais ne gérer qu’une fois cette problématique.

Inconvénients :

  • Risque d’oubli de génération du PCML dans l’IFS.
  • Risque d’écraser le PCML modifié par le PCML standard lors d’une prochaine compilation.
  • Risque de conflit entre les normes de nommage, ou de non utilisation de la norme.
  • Si vous avez plusieurs partitions, il faudra soit déployer le PCML dans l’IFS de chaque partition, soit le générer dans l’IFS puis le modifier via le programme.

Extraction des informations du PCML d’un objet :

Prérequis : génération du PCML dans l’objet, les options de compilation peuvent être dans le source.

Cette méthode consiste à :

  • Extraire les informations du PCML, via l’API QBNRPII. Les informations relatives aux paramètres seront stockés dans fichier.
  • Gérer l’usage des paramètres trouvés, par un interactif.
  • Générer le paramètre -parameterUsage à ajouter à la commande shell de création de web service.

Programme d’extraction des informations PCML :

**free

/if defined(*crtbndrpg)
ctl-opt actgrp(*NEW) ;
/endif
ctl-opt option(*NODEBUGIO : *SRCSTMT);

//-------------------------------------------------------------------------------------------------/
// Paramètre PGM
Dcl-Pi *N;
 P_Objet Char(10) const;
 P_Lib Char(10) const;
 P_Type Char(10) const;
 P_Error Char(110);
End-Pi;

//-------------------------------------------------------------------------------------------------/
// DS objet et module pour appel APi
dcl-ds qualname qualified based(TypeDef);
 Obj Char(10);
 Lib Char(10);
end-ds;

dcl-ds Qbn_Interface_Entry_t qualified based(TypeDef);
 // Offset from start of receiver
 Offset_Next_Entry int(10) ;
 Module_Name Char(10) ;
 Module_Library Char(10) ;
 Interface_Info_CCSID int(10) ;
 Interface_Info_Type int(10) ;
 Offset_Interface_Info int(10) ;
 Interface_Info_Length_Ret int(10) ;
 Interface_Info_Length_Avail int(10) ;
end-ds;

dcl-ds Qbn_PGII0100_t qualified based(TypeDef);
 Bytes_Returned int(10) ;
 Bytes_Available int(10) ;
 Obj_Name Char(10) ;
 Obj_Lib_Name Char(10) ;
 Obj_Type Char(10) ;
 Reserved3 Char(2) ;
 Offset_First_Entry int(10) ;
 Number_Entries int(10) ;
end-ds;

dcl-ds errcode qualified;
 bytesprov int(10) inz(0);
 bytesavail int(10) ;
end-ds;

dcl-ds psds psds ;
 codmsg char(7) pos(40);
 Libmsg char(100) pos(91);
end-ds;

// Define the initial storage for the first call to the API
dcl-ds tempRcvr likeds(Qbn_PGII0100_t);
dcl-ds rcvr likeds(Qbn_PGII0100_t) based(pRcvr);
dcl-ds entry likeds(Qbn_Interface_Entry_t) based(pEntry);
dcl-ds NomObj likeds(qualname);
dcl-ds NomMod likeds(qualname);

//-------------------------------------------------------------------------------------------------/
// Constantes
dcl-c AllMod '*ALLBNDMOD' ;
dcl-c LibModPGM 'QTEMP' ;
dcl-c Format_retour 'RPII0100' ;
// tailleXml doit avoir la même valeur que la variable XMLVALUE
dcl-c tailleXml 8192 ;

//-------------------------------------------------------------------------------------------------/
// Variables
dcl-s pRcvr pointer inz(*null);
dcl-s data Char(tailleXml) based(pData);
dcl-s pcml Varchar(tailleXml);
dcl-s len int(10);
dcl-s i int(10);
dcl-s entry_off int(10);

//-------------------------------------------------------------------------------------------------/
// Prototype d'appel de l'API QBNRPII (Retrieve Program Interface Information)
dcl-pr QBNRPII extpgm('QBNRPII') ;
 Receiver_variable likeds(Qbn_PGII0100_t);
 Length_of_receiver_variable int(10) const;
 Format_name Char(8) const;
 Qualified_object_name likeds(qualname) const;
 Object_Type Char(10) const ;
 Qualified_bound_module_name likeds(qualname) const;
 Error_code likeds(errcode);
end-pr;

// Prototype d'initialisation SQL
dcl-pr initSQL end-pr;

// Prototype déallocation mémoire
dcl-pr cleanup end-pr;

// Prototype extraction paramètre
dcl-pr rtvparam ind end-pr;

//-------------------------------------------------------------------------------------------------/
 initSQL();

// Construction des DS objet et module pour l'appel de l'API
 NomObj.Obj = P_Objet ;
 NomObj.Lib = P_Lib ;
 if P_Type = '*PGM' ;
 // Dans le cas d'un programme, nom module = nom PGM et bib = QTEMP
 NomMod.Obj = P_Objet ;
 NomMod.Lib = LibModPGM ;
 else;
 // Dans le cas d'un programme de service, appel de l'API pour tous les modules
 NomMod.Obj = AllMod ;
 NomMod.Lib = *blanks ;
 endif;

// Appel de l'API - détermination de la taille du retour contenant l'ensemble des PCML
 callp(e) QBNRPII (tempRcvr : %size(tempRcvr) : Format_retour : NomObj : P_Type : NomMod : errcode);

// Non trouvé : objet ou programme ou bibliothèque
 if %error;
 P_Error = codmsg + ' - ' + libmsg ;
 cleanup() ;
 return;
 endif;

// Pas de PCML
 if tempRcvr.Bytes_Available <= tempRcvr.Bytes_Returned;
 P_Error = 'Aucun PCML trouvé dans l''objet ' + P_Objet + ' de la bibliothèque ' + P_Lib ;
 cleanup() ;
 return;
 endif;

pRcvr = %alloc(tempRcvr.Bytes_Available);
 callp(e) QBNRPII (rcvr : tempRcvr.Bytes_Available : Format_retour : NomObj : P_Type : NomMod : errcode);
// Normalement, aucune erreur à ce niveau, mais par sécurité on refait les tests
 if %error or rcvr.Number_Entries = 0 ;
 if codmsg = *blanks ;
 P_Error = 'Erreur API - aucune donnée retournée';
 else;
 P_Error = codmsg + ' - ' + libmsg ;
 endif;

cleanup();
 return;
 endif;

// Lecture de la variable retour par PCML 
// --> Pour les *PGM, un seul PCML, pour les *SRVPGM, autant de PCML que de modules
 entry_off = rcvr.offset_First_Entry;
 for i = 1 to rcvr.Number_Entries;
 pEntry = pRcvr + entry_off;
 entry_off = entry.Offset_Next_Entry;
 pData = pRcvr + entry.Offset_Interface_Info;
 len = entry.Interface_Info_Length_Ret;

// Si le PCML récupéré est plus grand que la taille de la variable prévue --> erreur
 if len > tailleXml ;
 P_Error = 'Taille PCML : ' + %char(len) + ' trop grande pour la variable retour : ' + %editc(tailleXml : 'X') ;
 cleanup() ;
 return;
 endif;

pcml = %subst(data : 1: len);
 if not rtvparam() ;
 return;
 endif;
 endfor;

cleanup();
 P_Error = *blanks ;
 return;

//----------------------------------------------------------------------------------------//
// Procédure d'initialisation SQL //
//----------------------------------------------------------------------------------------//
dcl-Proc initSQL;
 Dcl-Pi *n end-Pi;

// initialisation des options de compilation sql
 EXEC SQL
 Set Option
 Naming = *Sys,
 Commit = *None,
 UsrPrf = *User,
 DynUsrPrf = *User,
 Datfmt = *iso,
 CloSqlCsr = *EndMod;

// création de la table de travail dans QTEMP, ou remise à blanc si elle existe déjà
 EXEC SQL
 create or replace table qtemp.wsparam
 (PROCNAME varchar(128),
 ENTRPT varchar(128),
 PARNAME varchar(128),
 USAGEIO varchar(11))
 on replace delete rows;

end-proc;

//----------------------------------------------------------------------------------------//
// Procédure de désallocation de la mémoire //
//----------------------------------------------------------------------------------------//
dcl-Proc Cleanup;
 Dcl-Pi *n end-Pi;

if pRcvr <> *null and pRcvr <> %addr(tempRcvr);
 dealloc(n) pRcvr;
 endif;

end-proc;

//----------------------------------------------------------------------------------------//
// Procédure d'extraction des paramètres //
//----------------------------------------------------------------------------------------//
dcl-Proc rtvparam;
 Dcl-Pi *n ind end-Pi;

dcl-s XMLVALUE sqltype(XML_CLOB : 8192);

// conversion du texte PCML en un champs XML
 EXEC SQL
 set :XMLVALUE = xmlparse(document :pcml) ;

if sqlcode <> 0 ;
 P_Error = 'Erreur de conversion XML, SQLCODE = ' + %char(sqlcode);
 cleanup() ;
 return *off;
 endif;

// Extraction du XML du nom du module, du nom de point d'entrée, du nom des paramètres et de leur usage,
// dans une table temporaire de QTEMP

EXEC SQL
 insert into qtemp.wsparam select x.* from
 xmltable('$d/pcml/program/data' passing :XMLVALUE
 as "d"
 columns PROCNAME varchar(128) path '../@name',
 ENTRPT varchar(128) path '../@entrypoint',
 PARNAME varchar(128) path '@name',
 USAGEIO varchar(11) path '@usage') as X ;
 if sqlcode <> 0 ;
 P_Error = 'Erreur insertion WSPARAM, SQLCODE = ' + %char(sqlcode);
 cleanup() ;
 return *off;
endif;

return *on ;

END-PROC;

A partir du fichier généré, vous pouvez gérer l’usage des paramètres :

Il ne reste plus qu’à créer le paramètre de la commande shell.

Extrait de code de la génération du paramètre -parameterUsage :

formalisme : -parameterUsage PROC1:i,o:PROC2:i,i,io

Attention à la casse des points d’entrée (PROC1 / PROC2…).

dcl-Proc gener_Opt;
 Dcl-Pi *n end-Pi;

Dcl-s LProc char(128);
 Dcl-s LEntrpt char(128);
 Dcl-s LParam char(128);
 Dcl-s LProcSh char(128);
 Dcl-s LUsage char(11);
 Dcl-s LProcSV char(30) inz(*blanks);
 Dcl-s param1 ind;
//------------------------------------//
// Constantes
dcl-c Deb_Opt '-parameterUsage ';
dcl-c SRVPGM '*SRVPGM' ;
//------------------------------------//

// Création d'un curseur récupérant les données du fichier de travail QTEMP/WSPARAM
 EXEC SQL
 declare curs02 cursor for
 SELECT PROCNAME, ENTRPT, PARNAME, USAGEIO
 FROM WSPARAM;

EXEC SQL
 open curs02;

// Lecture du fichier WSPARAM, pour alimentation de la variable retour
 dou sqlcode <> 0;
 EXEC SQL
 fetch from curs02
 into :LProc, :LEntrpt, :LParam, :LUsage;

if sqlcode = 0;
 //Entête module
 if LProc <> LProcSV ;
 // détermination du nom de la procédure pour la commande Shell (casse sensitive)
 if P_Type = SRVPGM ;
 LProcSh = LEntrpt ;
 else ;
 LProcSh = LProc ;
 endif;

// Insertion du nom de la procédure dans la commande
 if LProcSV = *blanks;
 P_Opt = Deb_Opt + %trim(LProcSh) +':'; // initialisation commande - 1ière proc
 else;
 P_Opt = %trim(P_Opt) + ':' + %trim(LProcSh) +':'; // Ajout 2ième procédure et suivan
 endif;
 LProcSV = LProc;
 param1 = *on ;
 endif;

//détail paramètre - construction de l'usage des paramètres
 if param1 ;
 param1 = *off ;
 else;
 P_Opt = %trim(P_Opt) + ',';
 endif;

select ;
 when LUsage = Input;
 P_Opt = %trim(P_Opt) + 'i';
when LUsage = Output;
 P_Opt = %trim(P_Opt) + 'o';
other;
 P_Opt = %trim(P_Opt) + 'io';
endsl;
 endif;
enddo;

EXEC SQL
 close curs02;

end-proc;

Dans le cas de web service de type REST, il faudra alimenter le fichier de propriété avec les bons usages des paramètres au lieu de construire le paramètre -parameterUsage.

Avantages :

  • Gestion de l’usage des paramètres au déploiement du web service.
  • Intégration simple au scénario de déploiement de web service ou autre traitement si besoin.
  • Dans le cas de sauvegarde du script de déploiement, il est directement utilisable sur une autre partition.

Inconvénients :

  • Aucun lorsqu’on a le source à disposition !

Cette dernière méthode sera intégrée à la prochaine version de notre outils WEBCONSOLE.

Retrouvez-nous à l’université du i les 16 et 17 mai.

Cette article est la suite de notre série sur le PCML

 

Nous avons vu que pour publier un programme en RPG ILE ou COBOL ILE . Le compilateur pouvait générer pour vous le PCML, mais si vous voulez publier un programme CLLE, vous devez faire vous même le PCML, ce n’est pas très compliqué à faire …

 

Pour vous faciliter la vie nous avons fait  un outil qui permet de publier des programmes en CLLE, en générant pour vous le PCML .

 

La commande s’utilise comme ceci

GENCLPCML FICSRC(QCLSRC) BICSRC(BERTHOIN) PGM(MONPGM) BIBPGM(BERTHOIN)

Vous devrez lui indiquer

  • le fichier source
  • la bibliothèque source
  • le programme
  • la bibliothèque du programme

Le paramètre IFS peut être laissé à *DFT, dans ce cas le fichier s’appelle nomdupgm.PCML et il est placé dans le répertoire courant

Le paramètre version est facultatif, par défaut il indiquera 6.0.

 

Limites d’utilisation

Vos paramètres doivent être sur la même ligne

PGM PARM(&PARM1 &PARM2 &PARM3)

La syntaxe suivante n’est pas reconnue :

PGM PARM(&PARM1 &PARM2 + 
 &PARM3)


Les paramètres doivent avoir la même casse :

PGM PARM(&PARM1 &PARM2 &PARM3) 
   DCL VAR(&PARM1) TYPE(*CHAR) LEN(10) 
   DCL VAR(&PARM2) TYPE(*DEC ) LEN(5 0) 
   DCL VAR(&PARM3) TYPE(*INT )

La syntaxe suivante n’est pas reconnue :

PGM PARM(&PARM1 &PARM2 &PARM3) 
  DCL VAR(&Parm1) TYPE(*CHAR) LEN(10) 
  DCL VAR(&pARM2) TYPE(*DEC ) LEN(5 0) 
  DCL VAR(&parm3) TYPE(*INT )

Le type des paramètres peut être uniquement *CHAR, *DEC, *INT

Exemple d’utilisation :

Le programme source suivant MONPGM

PGM PARM(&PARM1 &PARM2 &PARM3) 
   DCL VAR(&PARM1) TYPE(*CHAR) LEN(10) 
   DCL VAR(&PARM2) TYPE(*DEC ) LEN(5 0) 
   DCL VAR(&PARM3) TYPE(*INT )

Donnera comme PCML

<pcml version="6.0"> 
 <program name="MONPGM" path="/QSYS.LIB/BERTHOIN.LIB/MONPGM.PGM"> 
 <!-- Généré par GENCLPCML --> 
 <!-- CLLE program: MONPGM --> 
 <!-- created: 21/3/2018 09.16.42 --> 
 <data name="PARM1" type="char" length="10" usage="inputoutput" /> 
 <data name="PARM2" type="packed" length="5" precision="0" usage="inputoutput" />
 <data name="PARM3" type="char" length="1" usage="inputoutput" /> 
 </program> 
</pcml>

Complément :
Cette solution vous permet d’obtenir des paramètres en entrée et sortie, cas le plus usuel.

Nous avons ajouter un macro langage qui permet d’indiquer à GENCLPCML la nature de votre paramètre : vous pouvez indiquer sur la ligne de déclaration de votre variable les instructions suivantes

/*%%inp%%*/ pour un paramètre en input
/*%%out%%*/ pour un paramètre en output

Si vous n’indiquez rien votre paramètre sera en entrée/sortie

Soit le programme MONPGM

1 paramètre en entrée
1 en sortie
1 en entrée sortie

PGM PARM(&PARM1 &PARM2 &PARM3) 
DCL VAR(&PARM1) TYPE(*CHAR) LEN(10) /*%%inp%%*/ 
DCL VAR(&PARM2) TYPE(*DEC ) LEN(5 0) /*%%out%%*/ 
DCL VAR(&PARM3) TYPE(*INT )

Produira le PCML suivant

<pcml version="6.0"> 
 <program name="MONPGM" path="/QSYS.LIB/BERTHOIN.LIB/MONPGM.PGM"> 
 <!-- Généré par GENCLPCML --> 
 <!-- CLLE program: MONPGM --> 
 <!-- created: 21/3/2018 13.24.03 --> 
 <data name="PARM1" type="char" length="10" usage="input" /> 
 <data name="PARM2" type="packed" length="5" precision="0" usage="output" /> 
 <data name="PARM3" type="char" length="1" usage="inputoutput" /> 
 </program> 
</pcml>

Pour le récupérer l’outil

ci joint les sources  :  Télécharger le savf

  • La commande (GENCLPCML)
  • Le programme CLLE (GENCLPCMLC)
  • Le programme RPGLE (GENCLPCMLR)

Vous devez restaurer les sources et il suffit de les compiler en indiquant PGM(GENCLPCMLC)  pour la génération de la commande et de lancer celle-ci pour tester.

Le PCML (Program Call Markup Language), est un document généré par le compilateur pour décrire l’interface de vos programmes ou programmes de service. Il permet d’automatiser l’appel des programmes via Java ou d’autres langages.

En RPGLE,  il existe 2 méthodes pour déclarer des paramètres.

Première solution

En carte C, méthode que l’on trouve encore sur des programmes existants : les paramètres sont considérés comme en entrée/sortie dans le PCML.

C     *entry        plist                                      
C                   parm      Tempin                         10
C                   parm      Tempout                        10

PCML généré :

<pcml version="6.0">                                                      
   <!-- RPG module: TESTPCML  -->                                         
   <!-- created: 2018-03-14-09.22.22 -->                                  
   <!-- source: BERTHOIN/QRPGLESRC(TESTPCML) -->                          
   <!-- 2 -->                                                             
   <program name="TESTPCML" entrypoint="TESTPCML">                        
      <data name="TEMPIN" type="char" length="10" usage="inputoutput" />        
      <data name="TEMPOUT" type="char" length="10" usage="inputoutput" /> 
   </program>                                                             
</pcml>

Deuxième solution

En les prototypant soit en carte D soit en format full free.
On peut jouer en indiquant « const » sur les paramètres en entrée, ils seront bien uniquement en input dans le PCML.
Mais les autres paramètres resteront en entrée/sortie :

 D                 PI                        
 DTempin                         10    const 
 DTempout                        10         

ou

dcl-pi *n ;                  
  Tempin char(10) const ; 
  Tempout char(10) ;      
end-pi;  

PCML généré :

<pcml version="6.0">                                                      
   <!-- RPG module: TESTPCML  -->                                         
   <!-- created: 2018-03-14-09.22.22 -->                                  
   <!-- source: BERTHOIN/QRPGLESRC(TESTPCML) -->                          
   <!-- 2 -->                                                             
   <program name="TESTPCML" entrypoint="TESTPCML">                        
      <data name="TEMPIN" type="char" length="10" usage="input" />        
      <data name="TEMPOUT" type="char" length="10" usage="inputoutput" /> 
   </program>                                                             
</pcml>                                                               

En CBLLE, il existe une seule solution pour déclarer les paramètres

LINKAGE SECTION.                                                      
01 TEMPIN PIC X(10).                                                             
01 TEMPOUT PIC X(10).        
…                                               
PROCEDURE DIVISION USING TEMPIN  TEMPOUT.

Et les paramètres sont en entrée/sortie ….

 

 

Lors de la publication par l’interface web, on peut ajuster ces paramètres. Le plus souvent, en entrée ou en sortie, pour générer un WSDL efficace :

 

PCML obtenu après modification par l’outil de déploiement :

<pcml version="6.0">                                                      
   <!-- RPG module: TESTPCML  -->                                         
   <!-- created: 2018-03-14-09.22.22 -->                                  
   <!-- source: BERTHOIN/QRPGLESRC(TESTPCML) -->                          
   <!-- 2 -->                                                             
   <program name="TESTPCML" entrypoint="TESTPCML">                        
      <data name="TEMPIN" type="char" length="10" usage="input" />        
      <data name="TEMPOUT" type="char" length="10" usage="output" /> 
   </program>                                                             
</pcml>

 

La complexité intervient, dans le cas où l’on veut bypasser l’interface web, pour automatiser des déploiements en utilisant les scripts SH.
Nos outils permettent la modification du PCML sans intervention manuelle. Pour cela, il vous faudra générer le fichier PCML dans un fichier IFS, celui généré dans le module n’étant pas modifiable :

 CRTBNDRPG PGM(BERTHOIN/TESTPCML) 
           SRCFILE(BERTHOIN/QRPGLESRC) 
           PGMINFO(*PCML *STMF) 
           INFOSTMF('/home/PLB/testpcml.pcml')

Ces options ne peuvent pas être intégrés dans vos cartes H ou CTL-OPT

Vous devrez modifier le type de vos paramètres et indiquer votre fichier STMF dans la publication du webservice :

installWebService.sh                                               
   ...         
   -pcml '/home/PLB/testpcml_modifie.pcml'

 

 

Versions

PCML a subi quelques évolutions il en est actuellement la version 7 depuis fin 2017. La version usuelle est toujours la 6 aujourd’hui. Cette dernière est indiquée dans le tag PCML :

<pcml version="6.0">

 

Les principales évolutions

  • PCML version 2.0 or higher
    « entrypoint »
    « passby »
    « returnvalue »

 

  • PCML version 3.0 or higher
    « bidistringtype »
    « threadsafe »

 

  • PCML version 4.0 or higher
    « chartype »
    « epccsid »
    « trim »

 

  • PCML version 6.0 or higher
    « dateformat »
    « dateseparator »
    « timeformat »
    « timeseparator »

 

  • PCML version 7.0 or higher
    « varchar »

Remarque :

Vous n’obtenez pas la même syntaxe si vous générez directement le programme.

CRTBNDRPG ...

<program name="TESTPCML" path="/QSYS.LIB/BERTHOIN.LIB/TESTPCML.PGM"

ou si vous générez d’abord le module

CRTRPGMOD ...

<program name="TESTPCML" entrypoint="TESTPCML">

Soit pour un module

<pcml version="6.0">                                                     
   <!-- RPG module: TESTPCML  -->                                        
   <!-- created: 2018-03-14-09.22.22 -->                                 
   <!-- 2 -->                                                             
   <program name="TESTPCML" entrypoint="TESTPCML">                       
   ...
</pcml>

et pour un programme

<pcml version="6.0">                                                     
   <!-- RPG module: TESTPCML  -->                                        
   <!-- created: 2018-03-14-09.22.22 -->                                 
   <!-- source: BERTHOIN/QRPGLESRC(TESTPCML) -->                         
   <!-- 1 -->
   ...
</pcml>

 

 

La version par défaut est PCML 6, pour utiliser la version 7, vous devez le spécifier explicitement.

De plus, cela nécessite : PTF SI66150 (7.3) ou SI66149 (7.2) du 10/11/2017.

 

Pour l’utiliser

  • Soit par une variable d’environnement
  ADDENVVAR ENVVAR(QIBM_RPG_PCML_VERSION) VALUE(7.0) LEVEL(*SYS)
  • Soit par CTL-OPT
  ctl-opt pgminfo(*pcml:*module:*dclcase:*v7) ;

 

La différence V6/V7 porte sur la gestion des VARCHAR

V6 : VARCHAR considéré comme une DS

<struct name="STRIN" usage="input">
   <data name="length" type="int" length="2" precision="16" usage="inherit" />
   <data name="string" type="char" length="length" usage="inherit" />
</struct>

 

V7 : supporté nativement via le type « varchar »

<data name="STRIN" type="varchar" length="25" usage="input" />

 

Cet article est le premier d’une série à suivre sur le PCML, rendez-vous très vite !

La dernière version de ACS, 1.1.7.3 disponible depuis le 12 février 2018, apporte un certain nombre d’évolutions comme documenté ici par IBM :

 

En images, le résumé des nouveautés.

 

Base de données

Exécution de script SQL

Il est maintenant possible de personnaliser la bordure de la fenêtre. Très pratique quand vous ouvrez des sessions sur de multiples systèmes et/ou avec de multiples utilisateurs :

Gestion des journaux

Des options supplémentaires. A noter que la modification des droits est permise

Visual Explain

Le détail des option d’affichage est rappelé en dessous du graphique, avec reprise des codes couleurs.

Téléchargement des spoules

Choix de l’emplacement possible dans le fenêtre de téléchargement et plus uniquement dans les préférences globales.

 

IFS

Affichage du contenu avec gestion correcte des cssid

Affichage des droits

Non modifiable !

 

A très vite pour les prochaines évolutions.