Un chat en Pharo : le serveur

Magazine
Marque
GNU/Linux Magazine
Numéro
189
Mois de parution
janvier 2016
Spécialité(s)


Résumé
À l'aide de Teapot, Pharo permet la définition d'un serveur REST en quelques dizaines de lignes. Grâce à lui, vous allez développer rapidement un serveur de chat basé sur HTTP et permettant l'échange de messages entre plusieurs interlocuteurs.

Body

L'objectif de cet article est de vous faire développer en trois classes, un serveur de chat. Cette petite aventure vous permettra de vous familiariser avec Pharo et de voir l'aisance avec laquelle un serveur REST peut être défini. Développée en quelques heures, TinyChat a été conçue comme une application pédagogique.

Avant toute chose, téléchargez Pharo à partir du site officiel du projet (www.pharo.org). Pharo est principalement disponible pour Linux, Mac et Windows. L'installation se résume à la décompression d'un fichier zip. Une fois Pharo lancé, vous devez lui ajouter le framework Teapot.

Vous pouvez charger Teapot en utilisant le  Configuration Browser qui se trouve dans le menu Tools du menu principal. Sélectionnez Teapot et utilisez Install Stable. Une autre solution consiste à utiliser le script suivant :

Gofer it

 smalltalkhubUser: 'zeroflag'

 project: 'Teapot';    

 configuration;    

 loadStable.

Votre environnement de développement est maintenant prêt. La première étape va consister à mettre en place l'élément de base d'un chat : les messages.

1. Architecture et représentation des messages

Nous allons donc construire un serveur de discussion. La communication entre le serveur et son client sera basée sur HTTP et REST. La classe principale sera TCServer. Nous définirons également deux autres classes : la classe TCMessage qui représente les messages échangés (dans le futur, vous pourrez étendre TinyChat pour échanger des éléments plus structurés comme JSON ou STON qui est le format textuel Pharo) et la classe TCMessageQueue qui stocke les messages.

1.1 La classe TCMessage

Un message est un objet très simple avec un texte et un identifiant pour l'émetteur.

Nous définissons la classe TCMessage dans le package TinyChat :

Object subclass: #TCMessage

 instanceVariableNames: 'sender text separator'  

 ClassVariableNames: ''

 category: 'TinyChat'

code

Les variables d'instances sont les suivantes :

- sender : le login de l'expéditeur ;

- text : le texte du message ;

- separator : un caractère séparateur pour l'affichage.

1.2 Gestion des variables d'instance sender et text

Nous créons les accesseurs suivants :

TCMessage >> sender

 ^sender

TCMessage >> sender: anObject

 ^sender := anObject

TCMessage >> text

 ^ text

TCMessage >> text: anObject

 text := anObject

1.3 Initialisation de la classe

La méthode initialize définit la valeur du caractère séparateur :

TCMessage >> initialize

 super initialize.

 separator := '>'.

La méthode de classe TCMessage class>>from:text: permet d'instancier un message :

TCMessage class >> from: aSender text: aText  

 ^ self new

  sender: aSender;

  text: aText;

  yourself

Le message yourself rend le receveur du message : c'est une manière de s'assurer que le nouvel objet créé sera bien retourné par le message from:text: et non le résultat du message text:.

1.4 Convertir un message en chaîne de caractères

Nous ajoutons une méthode printOn: pour transformer le message en une chaîne de caractères. Celle-ci est constituée des éléments suivants :

- l'identifiant de l'expéditeur ;

- le caractère séparateur ;

- le texte envoyé ;

- un retour à la ligne.

La méthode printOn: est invoquée par la méthode printString. Il est important de comprendre que la méthode printOn: est invoquée par les outils tels que le débogueur ou l'inspecteur d'objets. L'utilisation de printOn: dans vos projets Pharo constitue une aide précieuse pour la mise au point de votre code . En adoptant un formatage adapté, elle facilite considérablement la lecture des données gérées dans votre application.

TCMessage >> printOn: aStream

 aStream

  << self sender;

  << separator;   

  << self text;

  << String crlf

1.5 Construire un message à partir d'une chaîne de caractères

Nous devons également définir deux méthodes pour créer un message à partir d'une chaîne ayant, par exemple, la forme: 'olivier>tinychat est cool'. Tout d'abord, créons une méthode de classe qui sera invoquée de la manière suivante: TCMessage fromString: 'olivier>tinychat est cool', puis la méthode d'instance remplissant les variables de l'objet préalablement créé.

TCMessage class >> fromString: aString  

 ^ self new

  fromString: aString;

  yourself

TCMessage >> fromString: aString

 "Compose a message from a string of this form 'sender>message'."

 | items |

 items := aString subStrings: separator.

 self sender: items first.

 self text: items second.

Maintenant, nous sommes prêts pour la définition du serveur REST.

2. Gestion des messages

Dans le serveur, nous allons ajouter une classe pour gérer une queue de messages. Ce n'est pas vraiment nécessaire, mais cela permet de bien identifier les responsabilités.

2.1 Stockage des messages

Créez la classe TCMessageQueue dans le package TinyChat-Server :

Object subclass: #TCMessageQueue

instanceVariableNames: 'messages'

classVariableNames: ''

category: 'TinyChat-server'

La variable d'instance messages est une collection ordonnée dont le contenu est composé d'instances de TCMessage. Une OrderedCollection est une collection qui s'agrandit dynamiquement lors d'ajouts.

TCMessageQueue >> initialize

 super initialize.

 messages := OrderedCollection new.

2.2 Opérations de base sur la liste des messages

On doit pouvoir ajouter un message avec add:, effacer la liste avec reset et connaître le nombre de messages avec size :

TCMessageQueue >> add: aMessage

 messages add: aMessage

TCMessageQueue >> reset

 messages removeAll

TCMessageQueue >> size

 ^ messages size

2.3 Obtenir la liste des messages à partir d'une position

Lorsqu'un client demande au serveur la liste des derniers messages échangés, il indique au serveur l'index du dernier message qu'il connaît. Le serveur répond alors la liste des messages reçus depuis cet index.

TCMessageQueue >> listFrom: aIndex

 ^ (aIndex > 0 and: [ aIndex <= messages size])

  ifTrue: [ messages copyFrom: aIndex to: messages size ]

  ifFalse: [ #() ]

2.4 Formatage des messages

La classe TCMessageQueue doit pouvoir formater une liste de messages (à partir d'un index) en une chaîne de caractères que le serveur pourra transmettre au client. On ajoute ensuite une méthode à la classe TCMessageQueue pour construire une seule chaîne de caractères à partir de chaque chaîne de caractères produite par chaque message :

TCMessageQueue >> formattedMessagesFrom: aMessageNumber

 ^ String streamContents: [ :formattedMessagesStream |

  (self listFrom: aMessageNumber) do: [ :m |

   formattedMessagesStream << m printString ]]

3. Le Serveur de Chat

Le cœur du serveur est basé sur le framework REST Teapot, permettant l'envoi et la réception des messages. Il maintient également une liste de messages qu'il communique aux clients.

3.1 Définition du routage HTTP

Créez la classe TCServer dans le package TinyChat-Server :

Object subclass: #TCServer

 instanceVariableNames: 'teapotServer messagesQueue'

 classVariableNames: ''

 category: 'TinyChat-Server'

La variable d'instance messagesQueue référence la liste des messages reçus et envoyés par le serveur :

TCServer >> initialize

 super initialize.

 messagesQueue := TCMessageQueue new.

La variable d'instance teapotServer référence l'instance du serveur TeaPot que l'on crée à l'aide de la méthode initializePort:.

TCServer >> initializePort: anInteger

 teapotServer := Teapot configure: {

 #defaultOutput -> #text.

 #port -> anInteger.

 #debugMode -> true}.

 teapotServer start.

Le routage HTTP est défini dans la méthode registerRoutes. Trois opérations sont définies :

- GET messages/count : retourne au client le nombre de messages reçus par le serveur ;

- GET messages/<id:IsInteger> : le serveur retourne les messages à partir de l'index indiqué dans la requête HTTP ;

- POST /message/add : le client envoie un message au serveur.

TCServer >> registerRoutes

 teapotServer

 GET: '/messages/count' -> (Send message: #messageCount to: self);

 GET: '/messages/<id:IsInteger>' -> (Send message: #messagesFrom: to: self);

 POST: '/messages/add' -> (Send message: #addMessage: to: self)

Nous exprimons ici que le chemin /message/count va donner lieu à l'exécution du message messageCount sur le serveur lui-même. Le pattern <id:IsInteger> indique que l'argument doit être exprimé sous forme de nombre et qu'il sera converti en un entier. La gestion des erreurs est construite dans la méthode registerErrorHandlers. Ici, on voit comment est construite une instance de la classe TeaResponse.

TCServer >> registerErrorHandlers

 teapotServer

  exception: KeyNotFound -> (TeaResponse notFound body: 'No such message')

Le démarrage du serveur est confié à la méthode TCServer class>>startOn: qui reçoit le numéro de port TCP en paramètre.

TCServer class >> startOn: aPortNumber

 ^self new

  initializePort: aPortNumber;

  registerRoutes;

  registerErrorHandlers;

  yourself

Il faut également gérer l'arrêt du serveur. La méthode stop met fin à l'exécution du serveur TeaPot et vide la liste des messages.

TCServer >> stop

 teapotServer stop.

 messagesQueue reset.

Comme il est probable que vous exécutiez plusieurs fois l'expression TCServer startOn:, nous définissons la méthode de classe stopAll qui stoppe tous les serveurs en récupérant toutes les instances de la classe.  La méthode TCServer class>>stopAll demande l'arrêt de chaque instance du serveur.

TCServer class >> stopAll

 self allInstancesDo: #stop

3.2 Traitements réalisés par le serveur

La méthode addMessage extrait de la requête du client le message posté. Elle ajoute à la liste des messages une nouvelle instance de TCMessage.

TCServer >> addMessage: aRequest

 messagesQueue add: (TCMessage from: (aRequest at: #sender) text: (aRequest at: #text))

La méthode messageCount retourne le nombre de messages reçus.

TCServer >> messageCount

 ^ messagesQueue size

La méthode messageFrom: retourne la liste des messages reçus par le serveur depuis l'index indiqué par le client. Les messages sont retournés au client sous la forme d'une chaîne de caractères. Ce point sera définitivement à améliorer (JSON ou XML sont plus adaptés si vous voulez faire évoluer ce projet).

TCServer >> messagesFrom: request  

 ^ messagesQueue formattedMessagesFrom: (request at: #id)

Nous en avons fini avec le serveur. À ce stade, nous pouvons le tester un peu. Commençons par le lancer :

TCServer startOn: 8181

Pour émettre des requêtes, nous pouvons utiliser un navigateur ou le client HTTP disponible par défaut dans Pharo (voir résultat en figure 1).

ZnClient new url: 'http://localhost:8181/messages/count' ; get

Les amateurs du shell peuvent également utiliser la commande curl :

curl http://localhost:8181/messages/count

figure_01

Fig. 1 : Le serveur n'a reçu aucun message.

Dans Pharo, nous pouvons aussi ajouter un message de la manière suivante :

ZnClient new

url: 'http://localhost:8181/messages/add';

formAt: 'sender' put: 'olivier';

formAt: 'text' put: 'Super cool ce tinychat';

post

Conclusion

Nous avons montré que la création d'un serveur REST est extrêmement simple avec Teapot. À ce stade, vous pouvez tester le bon fonctionnement du serveur et pourquoi pas, expérimenter en modifiant certaines méthodes ou en ajoutant de nouvelles fonctionnalités. Certes, le produit est encore brut de fonderie en termes de confort d'utilisation. Pour cette raison, dans le prochain article, vous réaliserez un client graphique pour le serveur. L'objectif sera de concevoir avec un minimum de code, un client de chat basé sur le framework Spec.

Références

[1] Site officiel du projet Pharo : http://www.pharo.org

[2] Site officiel du projet Zinc : http://zn.stfx.eu/zn/index.html

[3] Le framework Teapot :http://smalltalkhub.com/#!/~zeroflag/Teapot

[4] Télécharger le projet TinyChat : http://www.smalltalkhub.com/#!/~olivierauverlot/TinyChat

Pour aller plus loin

Le site du projet Pharo : http://www.pharo.org

L'ouvrage collectif « Pharo par l'exemple », Square Bracket Associates, 2011

L'ouvrage collectif « Deep inside Pharo », Square Bracket Associates, 2013




Article rédigé par

Par le(s) même(s) auteur(s)

Un chat en Pharo : le client

Magazine
Marque
GNU/Linux Magazine
Numéro
189
Mois de parution
janvier 2016
Spécialité(s)
Résumé
Poursuivons la découverte de Pharo et de quelques-uns de ses principaux frameworks. Vous avez déjà fait connaissance avec Teapot permettant de concevoir des services. Dans cet article, vous allez étudier la construction de requêtes HTTP à l'aide de Zinc [1] et construire une interface graphique à l'aide de Spec.

Une introduction aux sons en Pharo

Magazine
Marque
GNU/Linux Magazine
Numéro
178
Mois de parution
janvier 2015
Spécialité(s)
Résumé
Dans cet article, nous allons vous présenter une facette de Pharo qui n'est pas habituellement mise en avant. Ce domaine, c'est la synthèse sonore. Si nous avons besoin de sonoriser un peu notre tout nouveau programme ce qui nous vient à l'esprit c'est l'utilisation du mp3 pour jouer la musique en fond sonore ou des sons digitalisés pour les bruitages. Dans Pharo nous disposons d'outils et bibliothèques permettant de créer de toute pièce du son synthétisé pour nos productions. Nous allons vous montrer comment faire cela en quelques lignes de code seulement.

Les derniers articles Premiums

Les derniers articles Premium

La place de l’Intelligence Artificielle dans les entreprises

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

L’intelligence artificielle est en train de redéfinir le paysage professionnel. De l’automatisation des tâches répétitives à la cybersécurité, en passant par l’analyse des données, l’IA s’immisce dans tous les aspects de l’entreprise moderne. Toutefois, cette révolution technologique soulève des questions éthiques et sociétales, notamment sur l’avenir des emplois. Cet article se penche sur l’évolution de l’IA, ses applications variées, et les enjeux qu’elle engendre dans le monde du travail.

Petit guide d’outils open source pour le télétravail

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Ah le Covid ! Si en cette période de nombreux cas resurgissent, ce n’est rien comparé aux vagues que nous avons connues en 2020 et 2021. Ce fléau a contraint une large partie de la population à faire ce que tout le monde connaît sous le nom de télétravail. Nous avons dû changer nos habitudes et avons dû apprendre à utiliser de nombreux outils collaboratifs, de visioconférence, etc., dont tout le monde n’était pas habitué. Dans cet article, nous passons en revue quelques outils open source utiles pour le travail à la maison. En effet, pour les adeptes du costume en haut et du pyjama en bas, la communauté open source s’est démenée pour proposer des alternatives aux outils propriétaires et payants.

Sécurisez vos applications web : comment Symfony vous protège des menaces courantes

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Les frameworks tels que Symfony ont bouleversé le développement web en apportant une structure solide et des outils performants. Malgré ces qualités, nous pouvons découvrir d’innombrables vulnérabilités. Cet article met le doigt sur les failles de sécurité les plus fréquentes qui affectent même les environnements les plus robustes. De l’injection de requêtes à distance à l’exécution de scripts malveillants, découvrez comment ces failles peuvent mettre en péril vos applications et, surtout, comment vous en prémunir.

Bash des temps modernes

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Les scripts Shell, et Bash spécifiquement, demeurent un standard, de facto, de notre industrie. Ils forment un composant primordial de toute distribution Linux, mais c’est aussi un outil de prédilection pour implémenter de nombreuses tâches d’automatisation, en particulier dans le « Cloud », par eux-mêmes ou conjointement à des solutions telles que Ansible. Pour toutes ces raisons et bien d’autres encore, savoir les concevoir de manière robuste et idempotente est crucial.

Les listes de lecture

9 article(s) - ajoutée le 01/07/2020
Vous désirez apprendre le langage Python, mais ne savez pas trop par où commencer ? Cette liste de lecture vous permettra de faire vos premiers pas en découvrant l'écosystème de Python et en écrivant de petits scripts.
11 article(s) - ajoutée le 01/07/2020
La base de tout programme effectuant une tâche un tant soit peu complexe est un algorithme, une méthode permettant de manipuler des données pour obtenir un résultat attendu. Dans cette liste, vous pourrez découvrir quelques spécimens d'algorithmes.
10 article(s) - ajoutée le 01/07/2020
À quoi bon se targuer de posséder des pétaoctets de données si l'on est incapable d'analyser ces dernières ? Cette liste vous aidera à "faire parler" vos données.
Voir les 65 listes de lecture

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous