pause-café
destinée aux informaticiens sur plateforme IBM i.
Pause-café #71
JSON, Node.js et Python, RDI 9.5
Revenons sur le serveur de Web services.
lors du déploiement d'un pgm RPG en tant que web service PGMINFO permet une détection automatique des paramètres
Une PTF de 2009, apportait la possibilité de gérer en variable (integer uniquement) le nombre d'occurrences d'une structure
Depuis Juin 2015, SI56823 propose de rechercher automatiquement le nombre d'occurrences dans une variable placée avant la DS et nommée comme la DS suivit de _LENGTH
si vous décochez l'option, vous retrouvez l'affichage du dessus permettant de saisir le nbr d'occurrences
Cela permet de gérer les DS imbriquées :
- Exemple
Les producteurs d'une appellation (nombre variable, maxi 2000 producteurs par appellation)- contenant les vins de chaque producteur (nombre variable aussi, maxi 50 vins par producteur)
Apparait
- contenant les vins de chaque producteur (nombre variable aussi, maxi 50 vins par producteur)
- Les variables _LENGTH n'apparaissent pas dans le fichier wsdl.
- Les variables _LENGTH ne sont pas répercutées en tant que données retour
ici n producteurs en retour , le premier n'ayant que 2 vins
- cette technique peut être utilisée pour des variables caractères extrêmement longues (plus de 1000 c.)
- REPONSE_LENGTH
- REPONSE par ex. CHAR(10000), seule la partie significative de REPONSE sera transmise
- cette même PTF respecte l'ordre d'apparition des paramètres du pgm (ce n'était pas le cas avant)
- Enfin, vous avez le choix du format de sortie sur les web services REST
Exemple
Avec les tables résultat de CALL CREATE_SQL_SAMPLE
Format de sortie HTML
Le format de sortie par défaut reste JSON
Intégration JSON à DB2 en TR2.
Les niveaux de correctif pour Database level 5 (7.2) et 34 (7.1) de IBM i apportent à SQL l'intégration du langage
JSON.
JSON (JavaScript Object Notation) est un format de données textuelles dérivé de la notation des objets du langage JavaScript (Wikipedia) |
ce qui s'écrit comme cela en XML | s'écrit comme cela en JSON | |
<producteurs> <producteur> <numero>45</numero> <commune>Reims</commune> <appellation>13</appellation> </producteur> </producteurs> |
-> | { |
D'ailleurs vous trouverez ici deux fonctions bien pratiques XML2JSON et JSON2XML écrites en Java.
Un élément peut contenir un objet, une valeur ou un tableau de valeurs, marqué alors par [ et ]
<PO> <id>103</id> |
devient (remarquez le tableau de "item")
{ "PO":{ "id": 103, "orderDate": "2014-06-20", "customer": {"@cid": 888}, "items": { "item": [ { "partNum": "872-AA", "productName": "Lawnmower", "quantity": 1, "USPrice": 749.99, "shipDate": "2014-06-21" }, { "partNum": "837-CM", "productName": "Digital Camera", "quantity": 2, "USPrice": 199.99, "comment": "2014-06-22" } ] } } } |
Il y a trois manières d'utiliser JSON sur IBM i :
- DB2nosql -- accès "nosql" en mode ligne de commandes
- Les API Java
- les fonctions SQL :
- à l'origine SYSTOOLS.BSON2JSON / JSON2BSON
- maintenant de nouvelles fonctions (en technology preview) comme JSON_VAL et JSON_TABLE.
Pour l'accès en ligne de commandes, sous QSH, lancez /QIBM/ProdData/OS/SQLLIB/bin/db2nosql.
la première fois utilisez impérativement l'option -setup enable
Comme souvent, ce produit a des difficultés avec les codes pages "Latins"....
cela créé une table SYSJSON_INDEX et (s'il le faut) ré-enregistre les fonctions JSON/SQL dans SYSTOOLS
Pour accéder en mode commande, vous pouvez lancer QSH ou bien copier les fichiers suivants depuis /QIBM/ProdData/OS/SQLLIB/bin
sur votre poste :
- db2nosql (linux)
- db2nosql.bat (windows)
- jline-0.9.93.jar
- js.jar
- mongo-2.8.0.jar
- nosqljson.jar
- servlet-api.jar
- vous aurez aussi besoin de jt400.jar ( situé dans /QIBM/ProdData/os400/jt400/lib/java6)
Pour lancer
sous QSH
- /QIBM/ProdData/OS/SQLLIB/bin/db2nosql
- /QIBM/ProdData/OS/SQLLIB/bin/db2nosql -user profil -password motdepasse
depuis un PC
- db2nosql -hostname MONIBMi-user profil –password motdepasse
- db2nosql -url jdbc:as400:MONIBMi -user profil –password motdepasse
Attention tous les ordres sont sensibles à la casse.
db.sqlUpdate
passe un ordre SQL (par exemple create schema)
use schema
défini le schéma (bibliothèque) en cours
Cette dernière n'a pas forcement été créée par db2nosql
db.createCollection
créé une collection JSON (soit une table dans le schéma en cours)
cette table a la structure suivante
db.collection.insert
insert des données JSON dans une collection
C'est un BLOB (binaire) au format BSON
la fonction BSON2JSON est là pour afficher au format JSON (CLOB)
en 5250 il faudra ajouter CAST(systools.bson2json(data) as char(128) ) .....
sinon, vous verrez
Comme avec XML, la conformité du format JSON est vérifiée
de nombreuses commandes sont disponibles ensuite
Liste des commandes :
-- Commandes générales
Entrez 'help <méthode>' pour des informations détaillées. debug disable enable help use show
Syntaxe : "<method>"
-- Commandes de base de données
Entrez 'help db <méthode>' pour des informations détaillées.
createCollection dropAllCollections dropDatabase
getCollectionNames help
sqlQuery sqlUpdate stats
Syntaxe : "db.<method> "
-- Commandes des collections
Entrez 'help collection <méthode>' pour des informations détaillées.
aggregate count
distinct drop
ensureIndex exportFile find
findAndModify findOne help group importFile
insert markType remove
rename sampleSchema save
stats update
Syntaxe : db."collection".<méthode>
-- Commandes des curseurs
Entrez 'help cursor <méthode>' pour des informations détaillées.
batchSize help lazyFetch
limit size skip
sort
Syntaxe : "db.collection.find().<method> "
Exemples de commande : db.friends.insert({name:"Joe", age:5})
[Insérer dans la collection '"friends"'.]
db.friends.remove()
[Supprimer toutes les lignes de la collection '"friends"'.]
db.friends.find({name:"Joe"})
[Trouver des amis dont le nom est '"Joe"'.]
db.friends.find({age:{$gt:5}})
[Trouver des amis dont l'age est supérieur à "5".]
db.friends.find().sort({name:1})
[Trier par nom, par ordre croissant.]
db.friends.find().sort({name:-1}).limit(5)
[Trier par nom, par ordre décroissant, renvoyer les "5" premières lignes]
db.friends.find().sort({name:-1}).limit(5).skip(10)
[Trier par nom, par ordre décroissant, renvoyer les "5" premières lignes ignorer les "10" premières lignes.]
db.friends.importFile("C:\\myfriends.js")
[Importation de masse dans la collection]
quit
[Quitter l'interpréteur de commandes.]Pour plus d'informations, voir ce document (destiné à l'origine à DB2 sur autres plate-forme)
https://www.ibm.com/developerworks/data/library/techarticle/dm-1306nosqlforjson2/
Pour l'accès en java, voyez cette page https://www.ibm.com/developerworks/data/library/techarticle/dm-1307nosqlforjson3/
Pour se connecter
String url = "jdbc:as400://MONIBMi";
Connection con = DriverManager.getConnection(url, userid, password);
DB db = NoSQLClient.getDB(con, schema)
L'objet DB permet de passer des ordres SQL
db.SQLUpdate("CREATE SCHEMA JSONBIB");
L'objet DBCollection donnes accès à une collection au sens JSON
DBCollection jcol = db.getCollection("JSONCOL");
col.insert("{'nom':'Chateau Margaux','appellation':'Bordeaux', 'commune' : 'MARGAUX'}" )L'objet DBBasicObject offre un niveau d'abstraction avec un flux JSON
BasicDBObject json = new BasicDBObject();
json.append("nom","Coteaux de cherry");
json.append("appellation","Condrieu"); json.append("commune","Chavanay");
col.insert (json);
Pour l'accès en SQL, il faudra utiliser les fonctions SQL fournies
- JSON2BSON transforme du JSON (en clair) en format BSON (stockable)
Vous pouvez très bien créer une table sous SQL avec un champs BLOB ou VARCHAR et mettre du JSON dedans ( JSON2BSON impératif si c'est un BLOB)
CREATE TABLE JSONTXT( DATA VARCHAR(5000)) ;
INSERT INTO JSONTXT (data) VALUES ( systools.JSON2BSON('
{"nom":"Chateau Margaux","appellation":"Bordeaux", "commune" : "MARGAUX"} '
));
- BSON2JSON transforme du BSON en JSON
Exemple, lecture d'un flux JSON en RPG
Voyez ensuite le projet YAJL pour manipuler ce format en RPG
- BSON_VALIDATE, retourne 1 si le BSON est correct, 0 sinon
Exemplevalues systools.BSON_VALIDATE(systools.JSON2BSON('{
"PO":{
"id": 103,
"orderDate": "2014-06-20",
"customer": {"@cid": 888},
"items": {
"item": [
{ "partNum": "872-AA",
"productName": "Lawnmower",
"quantity": 1,
"USPrice": 749.99,
"shipDate": "2014-06-21"
},
{ "partNum": "837-CM",
"productName": "Digital Camera",
"quantity": 2,
"USPrice": 199.99,
"comment": "2014-06-22"
}
]
}
}
}'))
-> 1
Vous pouvez aussi utiliser les fonctions suivantes :
- JSON_VAL, Retourne la valeur d'un élément JSON
- paramètres
- JSON
- élément recherché
- type à retourner
les différents type admis en 3eme argument
type demandé type retourné commentaire 'n' DECFLOAT(34) 'i' INTEGER 'l' BIGINT 'f' DOUBLE 'd' DATE 'ts' TIMESTAMP 't' TIME 's:n' VARCHAR (n) n indique le nbr d'octets retournés (NULL si la longueur est supérieure à n). 'b:n' VARCHAR(n) FOR BIT DATA n indique le nbr d'octets retournés (NULL si la longueur est supérieure à n). 'u' INTEGER / 4 retourne 0 pour les éléments JSON vides, 1 si la valeur est fixée, NULL si l'élément n'est pas trouvé L'option na (No array) peut être ajoutée
- : na -> ne pas retourner si c'est un tableau (array)
- sans cette option, seule la première valeur est retournée
- paramètres
- JSON_TABLE, fait le même travail que XML_TABLE, parse le JSON pour en extraire les contenus,
- paramètres
- JSON
- élément recherché
- type à retourner
- paramètres
- retourne deux champs
- TYPE : type de donnée rencontré
Type
Description
1
un chiffre
2
Chaîne
3 Objet 4
Tableau (array)
8
Booléen
10
Null
16
Integer (binaire)
- VALUE, la valeur
- TYPE : type de donnée rencontré
- JSON_TABLE_BINARY, comme JSON_TABLE, mais retourne le format BSON (binaire)
donc la totalité de la chaîne en cas de tableau (occurrences)- paramètres
- JSON
- élément recherché
- type à retourner
- paramètres
- la fonction JSON_LEN, attend du BSON et retourne le nombre d'occurrences si c'est un tableau
- paramètres
- JSON
- tableau (élément) recherché
- paramètres
Exemple
CREATE TABLE POSAMPLE.JSONPO( ID INTEGER NOT NULL as identity, DATA BLOB(16M) DATCRT TIMESTAMP FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP ) ; |
INSERT INTO POSAMPLE.JSONPO (data) VALUES ( systools.JSON2BSON('{ |
select json_val(data , 'PO.customer.@cid' , 'f') from posample.JSONPO; |
select json_len(data , 'PO.items.item') from posample.JSONPO; |
- la fonction JSON_BINARY extrait un élément (comme JSON_VAL) même si celui contient un tableau
- paramètres
- JSON
- élément recherché
- longueur retour
On a alors la chaine entière au format BSON - paramètres
select json_binary(data , 'PO.items.item' , 256) from posample.JSONPO; |
- la fonction JSON_TYPE retourne le type de l'élément (voir JSON_TABLE)
- paramètres
- JSON
- élément recherché
- longueur à traiter
- paramètres
-
select json_type(data , 'PO.@id' , 5) from posample.JSONPO;
-> 16 select json_type(data , 'PO.items.item' , 256) from posample.JSONPO;
-> 4 - JSON_GET_POS_ARRAY_INDEX, doit permettre de retrouver l'index d'une valeur dans un tableau
- paramètres
- JSON
- QUERY
- paramètres
- JSON_UPDATE, doit permettre de modifier la valeur d'un élément
- paramètres
- JSON
- Opération (update, delete ?)
- élément à rechercher
- nouvelle valeur
- index dans le tableau
- paramètres
Enfin il y a deux autres fonctions, non documentées à ce jour
rappelez vous, c'est en technology preview ...
5733OPS : Node.js & Python
- IBM propose via un nouveau produit, l'intégration de projets Open source : 5733OPS
- A l'origine Node.JS
- en TR2, Python
- ...(bientôt le compilateur GCC ?)
Node.JS
Le produit peut être téléchargé depuis le site ESS
il s'installe par :
- RSTLICPGM 5733OPS OPTION(*BASE) LNG(*SAVVOL)
- RSTLICPGM 5733OPS OPTION(1) LNG(*SAVVOL)
suite à l'installation du produit, vous devez trouver ces répertoires
pour éviter l'erreur signal 5, vous devez aussi créer la variable d'environnement QIBM_MULTI_THREADED à 'Y'
enfin vous pouvez tester par QSH ou QP2TERM (PASE)
Pour une découverte du langage voyez OpenClassroom
Quelques notions quand même
node.js utilise beaucoup les fonctions, qui peuvent être externalisées :
la fonction doit utiliser exports pour être visible (comme les procédures en RPG)
le module "client" doit utiliser require pour "aller la chercher"
nb : la syntaxe javascript est automatiquement reconnue par RDI
Un pointeur vers une fonction peut être utilisé dynamiquement (comme un pointeur de procédure en RPG)
exemple, une fonction en tant que paramètre envoyé à une fonction
la fonction test recoit le pointeur vers une fonction (ici nommé param) puis lance la fonction param en mode asynchrone (plus tard dans le code))
Attention
écrivez test(param) > test recoit une variable (de type fonction)
et non test(param() ) > test receverait alors le résultat retourné par la fonction param
Ici le "pointeur" de la fonction est dans une variable, test continue à lancer la fonction qu'il a "reçu".
Elle n'a
pas de nom on parle de fonction anonyme.
Enfin une fonction peut-être passée directement en tant que paramètre, on parle de fonction "inline"
Une fonction ayant une fonction en tant que paramètre attend que la fonction "paramètre" soit terminée avant de s'exécuter. On parle alors de fonction asynchrone C'est aussi une technique évenementielle, la fonction est lancée à chaque fois que la fonction "paramètre" produit un résultat, très pratique pour un serveur http ! Ce concept s'appelle fonction de rappel ou callback. C'est véritablement une des spécificités de node.js |
Particulier, non ?
1er essai : Hello World (en Français)
saisissez ce code (ici par EDTF)
lancez le depuis QSH par :
et testez avec un navigateur
A chaque fois qu'un navigateur se connectera, la fonction inline sera lancée !
ce serveur peut aussi être soumis par
et aurait pu aussi être écrit comme ceci (probablement plus lisible)
ou encore
A chaque connexion, la fonction achaquevisiteur est lancée
2eme essai : simple requête SQL
remarquez db.conn qui se fait sur le nom de DataBase (celui indiqué par WRKRDBDIRE) ou *LOCAL
Résultat (ici sous PuTTY)
3eme essai : requête SQL et serveur HTTP
saisissez ce code
JSON.stringify transforme un objet JSON en chaîne de caractère
Soumettons le et testons (ce lien doit lancer lancer la page vue plus haut)
"As Usual", nous aurons sans doutes quelques soucis avec les caractères accentués "latin"
Le connecteur DB2 propose aussi d'autre fonctionnalités, particulièrement sur les métadata (Défintion des tables)
Vous pouvez vous connecter à la base locale, sans profil ni mot de passe (paramètres absents et non vides)
Résultat
Vous pouvez aussi accéder aux objets IBM i par un toolkit basé sur le projet XMLServices.
Exemple, avec une commande système RTVJOBA
Si vous le souhaitez, transformez la valeur en JSON (format plus "naturel" à node.js)
Exemple avec RTVSYSVAL sur notre n° de série
Data contient deux informations : un attribut et une valeur <data desc="QSRLNBR>217D81V</data>
En JSON une série de valeur est défine entre [ et ], c'est alors un objet tableau et non plus une valeur unitaire
Ce code fonctionne
Autre exemple avec un programme
Deux paramètres en entrée
- messages (15c.)
- valeur de pi (12 dt 11)
Lancement du pgm, récupération du résultat en XML (par défaut)
Lancement du pgm, transformation en JSON pour dérouler l'arbre
Deuxième partie : Python
Le produit peut toujours être téléchargé depuis le site ESS
il s'installe par :
- RSTLICPGM 5733OPS OPTION(*BASE) LNG(*SAVVOL)
- RSTLICPGM 5733OPS OPTION(2) LNG(*SAVVOL)
suite à l'installation du produit,et de la PTF SI57008, vous devez trouver ces répertoires
Testez
La PTF SI57253 apporte le connecteur DB2 qu'il faut ensuite installer par :
easy_install3 /QOpenSys/QIBM/ProdData/OPS/Python-pkgs/ibm_db/ibm_db-*.egg
La PTF SI57254 apporte le Toolkit, qu'il faut ensuite installer par :
easy_install3 /QOpenSys/QIBM/ProdData/OPS/Python-pkgs/itoolkit/itoolkit-*.egg
La PTF SI57255 apporte une passerelle FastCGI, nommé flipflop
easy_install3 /QOpenSys/QIBM/ProdData/OPS/Python-pkgs/flipflop/flipflop-*.egg
Enfin PTF SI57256 apporte un framework Web : Bottle
easy_install3 /QOpenSys/QIBM/ProdData/OPS/Python-pkgs/bottle/bottle-*.egg
Premier exemple : un mini serveur web (Python est un bon langage pour faire du CGI)
Pour un analyseur syntaxique de Python, voyez Pydev.org Sous RDI, lancer Aide/installer un nouveau logiciel Ajoutez un site d'installation Acceptez la licence |
En Python il n'y a pas d'accolade, un bloc (fonction, boucle, ...) commence par : et tant que le code est en retrait il appartient au bloc
L'indentation est obligatoire !
Encore une fois, nous vous conseillons le site OpenClassroom pour découvrir le langage
lancons le script
Exemple2 : affichage d'une page statique (mapage.html, externe)
ici, nous devons préciser le contexte "/test" à cause de @route('/test')
Exemple3 : Utilisation d'un formulaire
remarquez les importations (import)
la page contenant le formulaire (notez le contexte /question) et la réponse (/reponse)
->
- Accès à la base de données
Pour plus de détail voyez le connecteur DB2 pour Python
- Accès aux objets IBM i (PGM / cmd, etc) toujours via XMLSERVICES
- Attention, vous devez utiliser la toute dernière version du TOOLKIT
(A télécharger ou à mettre à jour donc, et non celui fourni via PTF ! )
- Attention, vous devez utiliser la toute dernière version du TOOLKIT
Exemple (RTVJOBA)
En mode Web
Pour plus de détails, voyez le Toolkit python, toujours base sur le projet XMLSERVICE
RDI : nouveautés de la version 9.5
1/ Installation
Téléchargez RDI 9.5 à l'adresse suivante :
dézippez le fichier obtenu et lancez installRDi.bat
2/ Nouveautés
• LE point remarquable est certainement l'intégration d'un émulateur 5250
Cliquez sur l'onglet propriété pour le paramétrage (CCSID à 297 ou 1147 en France)
Le bouton Connexion, lance l'émulateur qui s'affiche dans l'onglet Connexion Hôte
Vue d'ensemble
• Autre nouveauté : formatage de code
Soit le code suivant (pas très clean)
clic droit/source/Formater
Résultat
Tout cela étant paramétrable
• Les filtres peuvent être réordonnés
->
• ALT+S qui "split" la ligne, placait la nouvelle ligne colonne 1 (pas pratique en RPG qui commence en colonne 8)
Ce n'est plus le cas
• Vous pouvez être alerté quand il y a des mises à jour :
•Support de sources RPG de plus de 80 colonnes (TR3 obligatoire)
- nouvelles syntaxes SQL dont SELECT .... LIMIT 10 OFFSET 41 (affiche les lignes 41 à 50)
- de nombreuses nouvelles fonctions "DB2 as a service"
MEMORY_POOL() WRKSYSSTS
SYSTEM_STATUS() WRKSYSACT
OBJECT_LOCK_INFO WRKOBJLCK
RECORD_LOCK_INFO DSPRCDLCK
NETSTAT_INFO NETSTAT
NETSTAT_INTERFACE_INFO NETSTAT / option 1
NETSTAT_ROUTE_INFO NETSTAT / option 2NETSTAT_JOB_INFO NETSTAT / option 3
MEDIA_LIBRARY_INFO WRKMLBSTS
LICENSE_INFO WRKLICINF
OUTPUT_QUEUE_ENTRIES() WRKOUTQ
- 2 nouveaux composants dans ACS : "SQL Performance Center" et "Run SQL scripts" (version disponible en Décembre 2015)
- Nouvelle option aux produits Open Source : compilateur GCC
- DUPOPT pour dupliquer un média "bootable" y compris de format différent (DVD vers USB, par ex.)
- RPG
- Retrait de la restriction des 80 colonnes pour le compilateur RPG (il faut RDI 9.5 pour éditer)
- Véritable format libre :
En placant **FREE en début de source, toutes les lignes sont en format libre et peuvent commencer colonne 1
- Retrait de la restriction des 80 colonnes pour le compilateur RPG (il faut RDI 9.5 pour éditer)
TR3 annoncée le 5 Octobre
•voyez le détail de l'annonce ici
• https://t.co/fi15E21E5v
•Quelques remarques :
Copyright © 1995,2015 VOLUBIS