pause-café
destinée aux informaticiens sur plateforme IBM i.
Pause-café #78
SOA
Architecture SOA
Un web-service : c'est un logiciel qui interagis avec d'autres au moyen de protocoles universels (Http, Xml/Json, ...)
REST vs SOAP
La différence majeure est le degré de liaison entre le client et le serveur. Un client développé avec le protocole SOAP est étroitement lié au serveur, par un contrat (WSDL, comme une interface java). Si une modification est effectuée, l'ensemble peut ne plus fonctionner. Il faut effectuer des mises à jour du client s'il y a des changements sur le serveur.
Architecture REST
On dit qu'avec SOAP vous utilisez une enveloppe, avec REST une carte postale ;-)
REST (Representational State Transfert) ou RESTful est un style d’architecture permettant de construire des applications (Web, Intranet, Web Service). Il s’agit d’un ensemble de conventions et de bonnes pratiques à respecter et non d’une technologie à part entière. L’architecture REST utilise les spécifications originelles du protocole HTTP, plutôt que de réinventer une surcouche (comme le fait SOAP par exemple).
règles à suivre pour implémenter REST
Règle n°1 : l’URI comme identifiant des ressources
REST se base sur les URI (Uniform Resource Identifier) afin d’identifier une ressource. Ainsi une application se doit de construire ses URI (et donc ses URL) de manière précise, en tenant compte des contraintes REST. Il est nécessaire de prendre en compte la hiérarchie des ressources et la sémantique des URL pour les éditer :
Quelques exemples de construction d’URL avec RESTful :
Liste des producteurs
Non : http://ventedevins.com/producteur
Oui: http://ventedevins.com/producteurs
Filtre et tri sur les producteurs
Non : http://ventedevins.com/producteurs/filtre/madiran/tri/asc
Oui : http://ventedevins.com/producteurs?filtre=madiran&tri=asc
Affichage d’un producteur
Non : http://ventedevins.com/producteur/voir/87
Oui : http://ventedevins.com/producteur/87
Tous les vins d'un producteur
Non : http://ventedevins.com/producteur/vins/87
Oui : http://ventedevins.com/producteur/87/vins
Affichage d’un vin d'un producteur
Non : http://ventedevins.com/producteur/vin/87/1568
Oui : http://ventedevins.com/producteur/87/vin/1568
En construisant correctement les URI, il est possible de les trier, de les hiérarchiser et donc d’améliorer la compréhension du système.
L’URL suivante peut alors être décomposée logiquement :
http://ventedevins.com/producteur/87/vin/1568 => le vin 1568 du producteur 87
http://ventedevins.com/producteurs/87/vins => tous les vins du producteur 87
http://ventedevins.com/producteur/87 => un producteur
http://ventedevins.com/producteurs => tous les producteurs
Règle n°2 : les verbes HTTP comme identifiant des opérations
La seconde règle d’une architecture REST est d’utiliser les verbes HTTP existants plutôt que d’inclure l’opération dans l’URI de la ressource. Ainsi, généralement pour une ressource, il y a 4 opérations possibles (CRUD) :
- Créer (create)
- Afficher (read)
- Mettre à jour (update)
- Supprimer (delete)
HTTP propose les verbes correspondant :
- Créer (create) => POST
- Afficher (read) => GET
- Mettre à jour (update) => PUT
- Supprimer (delete) => DELETE
Exemple d’URL pour une ressource donnée (un livre par exemple) :
Créer un producteur
Non : GET http://ventedevins.com/producteur/create
Oui : POST http://ventedevins.com/producteur
Afficher
Non : GET http://ventedevins.com/producteur/voir/87
Oui : GET http://ventedevins.com/producteur/87
Mettre à jour
Non : POST http://ventedevins.com/producteur/editer/87
Oui : PUT http://ventedevins.com/producteur/87
Supprimer
Non : GET http://ventedevins.com/producteur/87/delete
Oui : DELETE http://ventedevins.com/producteur/87
Le serveur peut renvoyer les opérations acceptées sur une ressource via l’entête HTTP Allow.
Règle n°3 : les réponses HTTP comme représentation des ressources
Il est important d’avoir à l’esprit que la réponse envoyée n’est pas une ressource, c’est la représentation d’une ressource. Ainsi, une ressource peut avoir plusieurs représentations dans des formats divers : HTML, XML, CSV, JSON, etc.
C’est au client de définir quel format de réponse il souhaite reçevoir via l’entête Accept. Il est possible de définir plusieurs formats.
Quelques exemples :
Réponse en HTML
GET /producteurs
Host: ventedevins.com
Accept: text/html
Réponse en XML
GET /producteurs
Host: ventedevins.com
Accept: application/xml
Exemples
Les mails GMAIL sont accessibles depuis un navigateur (c'est même un peu le principe de base)• Mais aussi en web service REST :
-- lecture de mes mails chez google, penser à changer le mot de passe -;)
SELECT *
FROM XMLTABLE('$result/*[local-name()=''feed'']/*[local-name()=''entry'']'
PASSING
XMLPARSE(DOCUMENT
systools.HTTPGETBLOB('https://af400Volubis:motdepasse@gmail.google.com/mail/feed/atom/','')) AS "result"
COLUMNS
title VARCHAR(128) PATH '*[local-name()=''title'']',
summary VARCHAR(1024) PATH '*[local-name()=''summary'']',
author_name VARCHAR(255) PATH '*[local-name()=''author'']/*[local-name()=''name'']',
author_email VARCHAR(255) PATH '*[local-name()=''author'']/*[local-name()=''email'']' )
AS RESULT;
• Lecture des taux de change de l'Euro sur le site de la BCE
ici nous utilisons la fonction verbeuse de HttpGetClob afin de récupérer aussi les entêtes HTTP dans lesquelles il y a le code HTTP (200/404)
• retour en JSON
Avec des données OpenData de la ville de Nantes (voir http://data.nantes.fr)
• Watson
Découverte de l'API "translate"
Cette API, appelée depuis un navigateur, retourne du texte (par défaut) :
Exemple d'utilisation en PHP (inspiré de l'exemple trouvé sur http://yips.idevcloud.com/wiki/index.php/Watson/Watson)
Résultat
Autre test, cette fois en RPG, et en utilisant un DSPF ;-)
Résultat
Pour la partie serveur, nous avons plusieurs possibilités pour faire des API Rest :
- Faire des web services en PHP avec Zend
- Utiliser un des produits Open sources de 5733OPS
le projet DashforIBMi montre bien la puissance de Node.js, par exemple.
Il est basé sur une architecture modèle/vue/contrôleur
Les vues étant réalisées à l'aide jade , template de représentation (génération de html, déjà déprecié) - Utiliser le serveur d'applications intégré (IWS)
- ou le serveur de web services (même produit)
Au niveau serveur IBMi, tout cela est fortement basé sur "IBM i Services" (accès aux infos système en SQL)
Voici le résultat
Avec ce dernier, nous savons transformer des programmes RPG historiques en web services, voyez notre session de Mai 2017
En résumé :
•Nous nous connectons sur le serveur d'administration (JOB Admin) sur le port IP 2001
•Sur un serveur nouvellement créé, un service (convertTemp) est fourni en démo, pour créer les autres :
•Choix du type :
Et voilà
• Testons
Améliorations récentes
- V7R1M0 SF99368-Level 50
- V7R2M0 SF99713-Level 24
- V7R3M0 SF99722-Level 11
Serveur de web service en 2 tiers
- V7R1M0 SF99368-Level 52
- V7R2M0 SF99713-Level 26
- V7R3M0 SF99722-Level 13
SOAP Fault accessible pour les clients RPG utilisant des stubs pour accèder aux services SOAP
- V7R2M0 SI66149
- V7R3M0 SI66150
variables VARCHAR enfin supportées par PCML (qui passe en v7), donc par nos web services
Attention, il faut créer une variable d'environnement avant de compiler :
ADDENVVAR ENVVAR(QIBM_RPG_PCML_VERSION) VALUE(7.0) LEVEL(*SYS)
- TR4 (dispo fin du 1er trimestre)
- Possibilité d'utiliser l'utilisateur de connexion (authentification Apache)
- Possibilité de modifier/re-déployer un service
- Possibilité de rerouter le job correspondant dans un sous-système spécifique
Autres nouveautés de la TR4 (7.3) et TR8 (7.2) (lettre d'annonce)
- LE plus important, support des modèles S914/S924 à base de Power 9 (S922 avec VIOS uniquement)
- Les sources CL peuvent être placés dans l'IFS (CRTBNDCL et CRTCLMOD, mais aussi INCLUDE)
- Possibilité de faire des listes de distribution pour l'envoi de mails ( CHGSMTPA en mode *SMTP ou *SMTPMSF)
- Nouveau code opération RPG Data-Into, mariant XML-INTO et la notion de Handler de Rpg Open Access
- Voir l'annonce sur le Wiki RPG
- la documentation mise à jour
- En résumé, voici la syntaxe
data-into maDataStructure
%data('Exemple.csv' : 'doc=file case=any ccsid=job ')
%parser('*LIBL/MONPARSER'); - Vous devez écrire le parser ....
- un exemple est proposé pour le format JSON
(dans QOAR/SAMPLE, mais pour des raisons de CCSID copiez le dans un fichier à vous))
Utilisation de l'exemple
- un exemple est proposé pour le format JSON
- Nos premiers tests
- but du jeu : lire ce fichier (résultat de CPYTOIMPF)
- Programme utilisant le PARSER pour recevoir la donnée
- Résultat
- Le parser (DTAINTOPR1)
// exemple de parser pour des données structurées comme ceci : // code appellation, nom appellation, pays // extrait : // 1 ; "madiran" ; "France" // 2 ; "rioja" ; "Espagne" // etc...
ctl-opt main(parserMain) option(*srcstmt) dftactgrp(*No);
/copy QOAR/QRPGLESRC,QRNDTAINTO
dcl-ds parseInfo_T qualified;
lineStartOffset int(10);
dataLen int(10); // Copy of data buffer length
pStartBuffer pointer; // Copy of intial buffer pointer
end-ds;
dcl-proc parserMain; // proc. principale
// on recoit
// parm.data = pointeur sur les données à traiter
// parm.env = pointeur sur des pointeurs de procédure pour le "callback"
// parm.handle = pointeur, handler à utiliser à chaque appel de fonction
// parm.userParm = pointeur vers paramètres utilisateur
// parm.datalen = lg des données à traiter
// parm.dataCcsid = ccsid des données à traiter
// parm.userParmsisNUllTerminated : *ON chaîne du langage C
// *OFF chaîne RPG
dcl-pi *n extpgm;
parm likeds(QrnDiParm_T) const;
end-pi;
// block de controle pour traitement d'une ligne
// (paramètre transmis à lireLIgne() ...)
dcl-ds parseInfo LikeDS(parseInfo_T) Inz;
// nom des zones
dcl-s name1 varchar(15) inz('appel_code');
dcl-s name2 varchar(15) inz('appellation');
dcl-s name3 varchar(15) inz('pays');
// variables pour stocker les valeurs extraites
dcl-s appel_code varchar(10);
dcl-s appellation varchar(30);
dcl-s pays varchar(20);
// ligne et positions des séparateurs
dcl-s ligne varchar(1024);
dcl-s separator1 int(5);
dcl-s separator2 int(5);
// on envoie parm.env qui est un pointeur sur une structure de pointeurs
// contenant les procédures à appeller en "callback"
// (l'info est fournie en tant que paramètre par data-into et réutilisée tel-que)
pQrnDiEnv = parm.env;
// block de contrôle renseigné avec pointeur vers data et lg reçus.
parseInfo.pStartBuffer = parm.data;
parseInfo.dataLen = parm.dataLen;
// début de traitement
QrnDiStart (parm.handle);
// début de tableau
QrnDiStartArray(parm.handle);
// lecture première ligne
ligne = lireLigne(parseInfo);
DoW ligne <> ''; // EOF ?
// recherche premier ";"
separator1 = %scan(';': ligne);
appel_code = %subst(ligne: 1: separator1 - 1);
separator2 = %scan(';': ligne:separator1 + 1);
// il peut ne pas y avoir de pays
if separator2 > 0 and separator2 < %len(ligne);
// on saute le " de début et de fin (d'où + 2)
appellation = %subst(ligne: separator1 + 2 :
separator2 - separator1 -2);
pays = %subst(ligne: separator2 + 2 );
else;
appellation = %subst(ligne: separator1 + 2 );
pays = *blanks;
endif;
// Début de structure (une occurence)
QrnDiStartStruct (parm.handle);
// nom de la donnée extraite 'appel_code'
QrnDiReportName (parm.handle
: %addr(name1: *data)
: %len(name1) );
// valeur de la donnée extraite 'appel_code'
QrnDiReportValue(parm.handle
: %addr(appel_code: *data)
: %len(appel_code) );
// nom de la donnée extraite 'appellation'
QrnDiReportName (parm.handle
: %addr(name2: *data)
: %len(name2) );
// valeur de la donnée extraite 'appellation'
QrnDiReportValue(parm.handle
: %addr(appellation: *data)
: %len(appellation) );
// nom de la donnée extraite 'pays'
QrnDiReportName (parm.handle
: %addr(name3: *data)
: %len(name3) );
// valeur de la donnée extraite 'pays'
QrnDiReportValue(parm.handle
: %addr(pays: *data)
: %len(pays) );
// Fin de structure
QrnDiEndStruct (parm.handle);
// lecture ligne suivante
ligne = lireLigne(parseInfo);
EndDo;
// Fin de tableau
QrnDiEndArray(parm.handle);
// fin du traitement
QrnDiFinish(parm.handle);
on-exit;
// voir si besoin (pas ici)
end-proc;
// lecture d'une ligne (jusqu'à "retour chariot" dans le buffer) // on recoit
// - un offset (décalage) pour naviguer
// - lg des données à traiter
// - un pointeur sur la donnée à traiter
dcl-proc lireLigne;
dcl-pi *n varchar(1024);
parseInfo LikeDS(parseInfo_T);
end-pi;
// variable de travail de 1024 "plaquée" sur les données
// aucune ligne ne doit dépasser 1024 c.
dcl-s buffer char(1024) based(precordStart);
dcl-s record varchar(1024);
dcl-s endOfLine int(5);
If parseInfo.lineStartOffset < parseInfo.dataLen; // encore de la data ?
// Début de ligne = pointeur en cours + offset
precordStart = parseInfo.pStartBuffer + parseInfo.lineStartOffset;
// recherche fin de ligne
endOfLine = %scan(x'25': buffer);
// FindeLigne trouvé ?
// "record" renseigné et position modifiée pour prochaine fois
if endOfLine > 0;
record = %Subst(buffer: 1: endOfLine - 1);
parseInfo.linestartOffset += endOfLine; // prochaine ligne
else; // pas de data
record = '';
endif;
else; // Fin du buffer
record = '';
endif;
return record;
end-proc; - si la DS est incomplete : manque des zones (ici le nom de l'appellation)
- Vous recevez une erreur
- Sauf à le prévoir (comme avec XML-INTO) par Allowextra=yes
- Allowmissing permet d'avoir des zones dans la DS non retournées par le parser
- Si vous ne pouvez pas prédire le nombre d'occurences
- %Handler (comme XML-INTO) permet d'indiquer une procédure à appeler de manière récursive.
- %Handler (comme XML-INTO) permet d'indiquer une procédure à appeler de manière récursive.
- but du jeu : lire ce fichier (résultat de CPYTOIMPF)
- Le produit programme 5733OPS permet l'installation simplifiée de produits Open sources
- Node.JS
- Python
- Git
- Orion
- Nginx
Mais - Vous pouvez aussi, désormais, obtenir de nouveaux produits open source sous forme de RPM
- Lancez (par le gestionnaire scripts) bootstrap.sql si votre serveur IBMi à accès à Internet
ftp://public.dhe.ibm.com/software/ibmi/products/pase/rpms/bootstrap.sql
- Sinon
- téléchargez
- Transférer ces fichiers dans la répertoire /tmp (en binaire)
- passez la commande :
QSH CMD('touch -C 819 /tmp/bootstrap.log; /QOpenSys/usr/bin/ksh /tmp/bootstrap.sh > /tmp/bootstrap.log 2>&1')
- Sous QOpenSys/pkgs/bin
- yum list installed, liste les package installés
- yum list available, liste les package disponibles
- yum list all, liste tous les packages
- puis
- yum install <package>
- yum remove <package>
- yum search <package>
- Enfin dernière version d'ACS (1.1.7.3)
- Choix de l'emplacement du spool à la sauvegarde(ctrl+k)
- Affichage du contenu des fichiers de l'IFS
- Gestion des droits
- Plus d'options sur la gestion des journaux
- des notifications pour rappeler les options d'affichage choisies sous Visual Explain
- Plus d'exemples proposés sous "IBM i Services" (Edition / Insertion à partir d'exemples)
- Générez automatiquement une liste des zones dans la gestionnaire de scripts SQL
- Enfin, je vous rappelle que vous pouvez paramétrer bordure et texte pour vous souvenir du système cible.
Copyright © 1995,2018 VOLUBIS