Réconciliation par consensus des mises à jour des répliques partielles d'un document structuré.( Télécharger le fichier original )par Milliam Maxime Zekeng Ndadji Université de Dschang - Master 2 2016 |
Chapitre4Un prototype d'éditeur coopératifdésynchronisé (TinyCE v2)Sommaire 4.1 - Architecture et fonctionnement de TinyCE v2 46 4.2 - Le matériel de construction de TinyCE v2 52 4.3 - Mise en oeuvre d'un workflow d'édition sous TinyCE v2 53 4.4 - Synthèse 54 Les travaux que nous menons, visent essentiellement l'automatisation des procédures d'entreprises et des travaux coopératifs reposant sur l'édition coopérative des documents structurés. Ainsi, notre dénouement ne saurait être purement théorique; il doit aussi être technologique et donc pratique. À cet effet, nous avons entrepris de construire un outil de conception et de mise en oeuvre des CSCW basés sur l'édition coopérative des documents structurés. Nous poursuivons ainsi un objectif énoncé dans [TT09] et démarré avec le prototype TinyCE1 (prononcer"tennesse") [TT10]; d'ailleurs l'outil que nous mettons en oeuvre se nomme TinyCE v22 (que nous prononçons"taï-ni-ci-i version 2" 3) en hommage au prototype précédent. Un tel outil se devant d'être fiable, robuste, efficace, extensible et sécurisé, il est primordial de le concevoir minutieusement tout en reposant sur des outils (langages, bibliothèques, API - Application Programming Interface -, frameworks...) existants et présentant les caractéristiques recherchées. Nous présentons donc dans ce chapitre, les idées maîtresses pour la construction de TinyCE v2. TinyCE v2 est un éditeur permettant l'édition graphique et coopérative de la structure abstraite (arbre de dérivation) des documents structurés. Il s'utilise en réseau suivant un modèle client-serveur. Son interface utilisateur offre à l'usager des facilités pour l'édition de ses vues suivant les grammaires qui lui sont associées, l'édition et la validation d'une réplique partielle. Bien plus, cette interface lui offre aussi des fonctionnalités lui permettant d'expérimenter les concepts de projection, d'expansion et de fusion consensuelle étudiés dans ce mémoire.
4.1. Architecture et fonctionnement de TinyCE v2 46 Mémoire - ZEKENG NDADJI Milliam Maxime LIFA FIGURE 4.1 - Le logo de TinyCE v2. Nous allons tout d'abord présenter l'architecture générale et le fonctionnement de Ti-nyCE v2 (sect. 4.1); puis nous présenterons les outils utilisés pour sa confection (sect. 4.2) et enfin nous déroulerons, à base de notre outil, un des exemples de workflows d'édition manipulés dans ce mémoire (sect. 4.3). 4.1. Architecture et fonctionnement de TinyCE v2L'architecture de TinyCE v2 implémente, en langage Java, le design pattern MVC (Model View Controller ou Modèle Vue Contrôleur en français) qui est une référence en matière de patron de conception (architectural). Dans cette architecture, les vues affichent des résultats et récupèrent des entrées (données et commandes) qu'elles transmettent aux modèles à travers les contrôleurs (qui les valident); les modèles réalisent les traitements (parsing, stockage, communication...) puis notifient les vues pour rafraîchissement. Sous TinyCE v2, le modèle, la vue et le contrôleur sont organisés comme suit: -- Le modèle est la pièce maîtresse réalisant le plus gros du travail. Il repose sur trois principaux éléments : des parseurs, un communicateur et un "interpréteur Haskell" né de la fertilisation croisée du langage purement orienté objet Java et du langage purement fonctionnel Haskell (sect. 4.1.1);
-- La vue donne une représentation graphique des documents et des commandes dans le but de permettre une interaction conviviale entre TinyCE v2 et ses utilisateurs. Elle dispose donc de parseurs pouvant réaliser la correspondance entre les données reçues et les données à afficher. Sous TinyCE v2, la vue présente les mêmes fonctionnalités qu'on soit en mode client ou en mode serveur (il n'existe qu'une seule vue). En effet, dans une édition centralisée, le site serveur réalise les opérations de synchronisation et de (re)distribution en plus des actions réalisées par les clients (éditions...); mais dans une version pair à pair, les opérations de synchronisation 4.1. Architecture et fonctionnement de TinyCE v2 47 Mémoire - ZEKENG NDADJI Milliam Maxime LIFA et de (re)distribution peuvent être réalisées sur n'importe quel site. Le mode client-serveur est donc un cas particulier du mode pair à pair. TinyCE v2 est conçu pour pouvoir supporter (dans le futur) des éditions en pair à pair; ce qui explique nos choix. La vue est entièrement codée en langage Java et se sert de quelques bibliothèques que nous présenterons dans la suite de ce chapitre (sect. 4.2); -- Le contrôleur quant à lui, est chargé d'établir le pont entre la vue et le modèle. La figure 4.2 (page 47) résume l'architecture de TinyCE v2 ci-dessus présentée. FIGURE 4.2 - L'architecture de TinyCE v2. 4.1.1. Fertilisation croisée Haskell-JavaTout comme TinyCE, TinyCE v2 est programmé en utilisant deux langages de programmation : Haskell (pour le moteur) et Java (pour le reste). L'utilisation de ce couple de langages permet de tirer le meilleur de chacun d'entre eux. Nous exploitons Java pour la facilité qu'il offre pour la mise en oeuvre des interfaces graphiques ainsi que pour tirer profit des avantages offerts par les langages à objets (robustesse, modularité...). Du langage Haskell nous tirons profit de son grand pouvoir d'abstraction, de son mode d'évaluation (paresseuse), de la modularité... Afin de faire coopérer en parfaite synergie ce couple de langages, nous exploitons la possibilité d'appeler un code exécutable à l'intérieur de programmes Java (la même chose est possible à partir du langage Haskell). En effet, nous démarrons un interpréteur Haskell (GHCi - the Glasgow Haskell Compiler interactive -) à partir de Java, puis nous lui passons au fur et à mesure les commandes à exécuter et nous récupérons en sortie les éventuels résultats et/ou erreurs. Nous présentons sommairement ci-dessous cette démarche. Implémentation du moteur Haskell L'implémentation GHC [GHC] de Haskell n'est pas qu'un compilateur. On peut donc y créer des fonctions (programmes) et les faire exécuter (interpréter) en mode interactif 4.1. Architecture et fonctionnement de TinyCE v2 48 Mémoire - ZEKENG NDADJI Milliam Maxime LIFA grâce à son module GHCi. Un programme Haskell sous GHC est un fichier, bien conçu, d'extension.hs semblable à celui ci-dessous enregistré par exemple dans un fichier dénommé helloGHC.hs : 1 module HelloGHC where 2 sayHello name = "Bonjour "++name Pour exécuter ce programme en mode interactif, il suffit de charger le module avec la commande :load ou son raccourci :l puis d'appeler la fonction sayHello de la manière suivante (où xxx est l'argument de l'appel) : 1 :load "helloGHC.hs" 2 sayHello xxx Exécution d'un code Haskell dans Java Java permet de lancer un code exécutable (quelconque) à partir d'un code java, et donc, de lancer l'interpréteur GHCi de GHC. Pour exécuter un programme en Java on peut se servir de la classe ILanguageRunner définie comme suit: 1 import java.io.*; 2 3 public class ILanguageRunner{ 4 protected Process process = null; 5 protected String language; 6 protected String command; 7 public ILanguageRunner(String language, String command){ 8 this.language = language; 9 this.command = command; 10 } 11 public void startExecProcess() throws IOException{ 12 ProcessBuilder processBuilder = new ProcessBuilder(command); 13 process = processBuilder.start(); 14 } 15 public void killExecProcess(){ 16 if(process != null) 17 process.destroy(); 18 } 19 public void setExecCode(String code) throws IOException { 20 OutputStream stdin = process.getOutputStream(); 21 OutputStreamWriter stdinWriter = new OutputStreamWriter(stdin); 22 try{ 23 stdinWriter.write(code); 24 }finally{ 25 try{stdinWriter.close();}catch(IOException e){} 26 try{stdin.close();}catch(IOException e){} 27 } 28 } 29 public void getExecErrors() throws IOException{ 30 InputStream stdout = process.getErrorStream(); 31 InputStreamReader stdoutReader = new InputStreamReader(stdout); 32 BufferedReader stdoutBuffer = new BufferedReader (stdoutReader); 33 StringBuffer errorBuffer = null; 34 try{ 35 String line = null; 36 while((line = stdoutBuffer.readLine()) != null){ 4.1. Architecture et fonctionnement de TinyCE v2 49 Mémoire - ZEKENG NDADJI Milliam Maxime LIFA 37 if (errorBuffer == null) 38 errorBuffer = new StringBuffer(); 39 errorBuffer.append(line); 40 } 41 }finally{ 42 try{stdoutBuffer.close();}catch(IOException e){} 43 try{stdoutReader.close();}catch(IOException e){} 44 try{stdout.close();}catch(IOException e){} 45 } 46 if(errorBuffer != null) 47 throw new IOException(errorBuffer.toString()); 48 } 49 public String getExecResult() throws IOException{ 50 InputStream stdout = process.getInputStream(); 51 InputStreamReader stdoutReader = new InputStreamReader(stdout); 52 BufferedReader stdoutBuffer = new BufferedReader(stdoutReader); 53 StringBuffer resultBuffer = null; 54 try{ 55 String line = null; 56 while((line = stdoutBuffer.readLine()) != null){ 57 if (resultBuffer == null) 58 resultBuffer = new StringBuffer(); 59 resultBuffer.append(line); 60 } 61 }finally{ 62 try{stdoutBuffer.close();}catch(IOException e){} 63 try{stdoutReader.close();}catch(IOException e){} 64 try{stdout.close();}catch(IOException e){} 65 } 66 if(resultBuffer != null) 67 return resultBuffer.toString(); 68 return null; 69 } 70 public String getCommand(){ 71 return command; 72 } 73 public void setCommand(String command){ 74 this.command = command; 75 } 76 public String getLanguage(){ 77 return language; 78 } 79 public void setLanguage(String language){ 80 this.language = language; 81 } 82 public String executeCode(String code) throws IOException{ 83 startExecProcess(); 84 setExecCode(code); 85 getExecErrors(); 86 String result = getExecResult(); 87 killExecProcess(); 88 return result; 89 } 90 } Dans cette classe, 4.1. Architecture et fonctionnement de TinyCE v2 50 Mémoire - ZEKENG NDADJI Milliam Maxime LIFA -- les attributs process, language et command (lignes 2, 3 et 4) représentent respectivement le processus Java permettant de lancer l'exécutable, le langage courant4 et le chemin d'accès à l'exécutable; -- la méthode startExecProcess (lignes 9 à 12) construit le processus Java à partir de la commande courante. La méthode killExecProcess (lignes 13 à 16) détruit le processus lorsque celui-ci existe; -- la méthode setExecCode (lignes 17 à 26) permet d'écrire sur l'entrée standard de l'interpréteur le code à exécuter (cela correspond au passage des arguments au programme exécuté); -- les méthodes getExecErrors (lignes 27 à 46) et getExecResult (lignes 47 à 67) permettent respectivement de récupérer les éventuelles erreurs survenues lors de l'exécution du code définit par setExecCode (les erreurs sont renvoyées sous forme d'exception Java) et de récupérer les éventuels résultats de l'exécution dudit code; -- enfin, la méthode executeCode (lignes 80 à 87) exécute un code quelconque. Elle démarre un processus, définit le code, récupère les erreurs s'il y en a, sinon elle récupère le résultat puis détruit le processus. Une utilisation de la classe ILanguageRunner ci-dessus, pour l'exécution du code Haskell en mode interactif par GHCi peut se faire à travers la classe HaskellRunner qui hérite de ILanguageRunner et dont le code est donné ci-dessous : 1 import java.io.IOException; 2 3 public final class HaskellRunner extends ILanguageRunner{ 4 public HaskellRunner(){ 5 super("Haskell", "ghci"); 6 } 7 @Override 8 public String getExecResult() throws IOException{ 9 String execResult = super.getExecResult(), tmpString; 10 if(execResult == null) 11 return null; 12 String[] tab = execResult.split("Prelude> "); 13 if(tab.length == 2) 14 tab = tab[1].split("\\*[a-zA-Z0-9_]{1,}>"); 15 StringBuilder goodResult = new StringBuilder(); 16 for(int i = 1; i < tab.length - 1; i++){ 17 tmpString = tab[i].trim(); 18 if(!tmpString.isEmpty()){ 19 goodResult.append(tmpString); 20 goodResult.append("\n"); 21 } 22 } 23 return goodResult.toString(); 24 } 25 @Override 26 public void setCommand(String command){} 27 @Override 28 public void setLanguage(String language){} 29 } 4. Notons que ce code est taillé pour l'exécution des interpréteurs interactifs de tous types et non pas seulement pour l'exécution de GHCi. 4.1. Architecture et fonctionnement de TinyCE v2 51 Mémoire - ZEKENG NDADJI Milliam Maxime LIFA Dans cette classe, on a fixé le nom de la commande à ghci (ceci implique que le chemin d'accès du répertoire contenant le programme GHCi doit être inscrit dans la variable d'environnement path) et celui du langage à Haskell (ces deux valeurs ne peuvent plus être changées : les redéfinitions des méthodes setCommand et setLanguage associées au fait que la classe HaskellRunner ne puisse pas être dérivée5 nous le garantissent.). On a redéfini la méthode getExecResult pour mieux formater le résultat en le débarrassant des chaînes superflues ajoutées par GHCi. On peut donc désormais, exécuter notre moteur Haskell grâce au code suivant:
L'exécution de ce code fournit le résultat suivant: 1 Le résultat de l'exécution est : "Bonjour Ange Frank et Chris Maxime" Si on remplace le code de la ligne 7 par le code String commande = " :load helloGHC.hs \nsayHello"; alors le résultat obtenu est désormais le suivant: 1 Des erreurs sont survenues lors de l'exécution des commandes. C'est donc une version de la classe HaskellRunner qui joue le rôle"d'interpréteur Haskell" au sein de TinyCE v2. Elle permet une communication bidirectionnelle entre le modèle de TinyCE v2 (codé en Java) et Haskell suivant un protocole basé sur du texte (chaînes de caractères bien formées suivant un codage à la XML) que nous avons mis en oeuvre. L'approche que nous employons diffère nettement de celle présentée dans [TT10]. Les principales différences entre ces approches sont quasiment les mêmes qui animent les débats portant sur les langages interprétés et les langages compilés; notre approche étant assimilée aux langages interprétés et celle de [TT10] étant assimilée aux langages compilés. En effet pour que notre approche fonctionne, il faut toujours que le site sur lequel TinyCE v2 s'exécute soit muni d'un interpréteur Haskell en plus de la machine virtuelle Java. En plus, le code du moteur Haskell sera toujours visible par les utilisateurs de Ti-nyCE v2 ce qui constitue un risque en matière de sécurité. Néanmoins cette approche présente l'avantage d'être portable (car il existe des interpréteurs Haskell pour toutes les plateformes), d'offrir la facilité de mise à jour du moteur Haskell et surtout la possibilité d'exploiter toute la puissance qu'offre le langage Haskell. De plus, sous TinyCE v2, le moteur Haskell n'est nécessaire que pour le fonctionnement en mode serveur; c'est à dire 5. Ceci à cause de l'utilisation du modificateur final. 4.2. Le matériel de construction de TinyCE v2 52 Mémoire - ZEKENG NDADJI Milliam Maxime LIFA qu'il ne sert qu'à faire de la synchronisation et de la (re)distribution. Même les parseurs ont tous été écrits en Java. Ainsi, dans une utilisation purement centralisée de TinyCE v2, seul le serveur aura besoin d'un interpréteur Haskell; sachant que les routines de vérification de la conformité des répliques partielles mises à jour vis à vis de leurs modèles respectifs, peuvent être écrites ou générées en Java (à partir du couple d'outils (f)lex et yacc/bison6 ou encore grâce à Xtext 7). |
|