Pause-Café Volubis

pause-café

rendez-vous technique
Pause-Café est une réunion technique
destinée aux informaticiens sur plateforme IBM i.
Elle a lieu 3 à 4 fois par an : en Bretagne et sur internet.

Pause-café #71

Novembre 2015

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



  • 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>
->

{
   "producteurs":  {
       "producteur": {
            "numero":45
            "commune":"Reims",
            "appellation":13,     
       }
    }
}


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>
<orderDate>2014-06-20</orderDate>
<customer>
<cid>888</cid>
</customer> <items>
<item>
<partNum>872-AA</partNum>
<shipDate>2014-06-21</shipDate>
<productName>Lawnmower</productName>
<USPrice>749.99</USPrice>
<quantity>1</quantity>
</item>
<item>
<partNum>837-CM</partNum>
<productName>Digital Camera</productName>
<USPrice>199.99</USPrice>
<quantity>2</quantity>
<comment>2014-06-22</comment>
</item>
</items>
</PO>

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


    Exemple

    values 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





  • 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


    • 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



  • 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





  • 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é



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('{
"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" } ] } } }'
));

 

select json_val(data , 'PO.customer.@cid' , 'f') from posample.JSONPO;

-> 888,00


select json_len(data , 'PO.items.item') from posample.JSONPO;

-> 2


  • 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

    select json_binary(data , 'PO.items.item' , 256) from posample.JSONPO;

    -> '040900000003....'



  • la fonction JSON_TYPE retourne le type de l'élément (voir JSON_TABLE)
    • paramètres
      • JSON
      • élément recherché
      • longueur à traiter


  • select json_type(data , 'PO.@id' , 5) from posample.JSONPO;
    -> 16 select json_type(data , 'PO.items.item' , 256) from posample.JSONPO;
    -> 4



  • Enfin il y a deux autres fonctions, non documentées à ce jour

    rappelez vous, c'est en technology preview ...

     

  • JSON_GET_POS_ARRAY_INDEX, doit permettre de retrouver l'index d'une valeur dans un tableau
    • paramètres
      • JSON
      • QUERY

  • 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

 

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 ! )

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 :

http://www-933.ibm.com/support/fixcentral/swg/downloadFixes?parent=ibm~Rational&product=ibm/Rational/Rational+Developer+for+i&release=All&platform=All&function=fixId&fixids=9.5.0.0-Rational-RDI-TRIAL-WEBINSTALL&includeRequisites=1&includeSupersedes=0&downloadMethod=http

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)


Dernière minute

Copyright © 1995,2015 VOLUBIS