Scala – Scalatra – Salat – MongoDB : découverte

CodeDevFrançaisTutorials

Bonjour, un petit test avec Scala (2.9.0-1) pour la partie service, Scalatra concernant le front en REST, Salat et MongoBB pour la persistance des données. J’utiliserai sbt (0.10) pour la gestion du projet et IntelliJ Idea comme IDE (What else ?).

Qu’allons nous voir ici ?

Nous allons exposer une API REST (et sa documentation associée) afin de créer des utilisateurs (identifiant, courriel, mot de passe). Cette API est extrèmement simple afin de bien comprendre ce qui se passe.
L’objectif est donc de pouvoir :
– créer un utilisateur,
– trouver un utilisateur (avec son ID ou son identifiant),
– effacer un utilisateur
– visualiser la documentation de chaque API en ligne pour chacune des méthodes

Installation de MongoDB :

wget -c http://www.mongodb.org/dr/fastdl.mongodb.org/linux/mongodb-linux-x86_64-1.8.2.tgz/download
tar xzf mongodb-linux-x86_64-1.8.2.tgz
rm -f mongodb-linux-x86_64-1.8.2.tgz
cd mongo
sudo mkdir -p /data/db/
sudo chown <code>id -u</code> /data/db
bin/mongod

Ok Mongo DB est lancé.

Exécutons le projet
Comme ça vous verrez ce que ça fait, puis ensuite nous mettrons un peu plus les mains dans le cambouis.

Voici le projet, soit vous le récupérez via github :

git clone git://github.com/jrevault/ScalaScalatraSalatMongoDB.git

Ou alors vous pouvez le télécharger :
https://github.com/jrevault/ScalaScalatraSalatMongoDB/zipball/master
Décompressez-le quelque part et ouvrez une ligne de commande et faites :

julien@mars:/data/DEV/ScalaScalatraSalatMongoDB$ sbt

Bien entendu si vous n’avez pas installé sbt ça ne marchera pas ! Dans ce cas, allez ici : https://github.com/harrah/xsbt/wiki/Setup

Bon alors, continuons, après avoir exécuté la commande sbt vous devriez avoir quelque chose du genre :

julien@mars:/data/DEV/ScalaScalatraSalatMongoDB$ sbt
Getting net.java.dev.jna jna 3.2.3 ...
:: retrieving :: org.scala-tools.sbt#boot-app
        confs: [default]
        1 artifacts copied, 0 already retrieved (838kB/36ms)
Getting Scala 2.8.1 (for sbt)...
:: retrieving :: org.scala-tools.sbt#boot-scala
        confs: [default]
        4 artifacts copied, 0 already retrieved (15296kB/315ms)
Getting org.scala-tools.sbt sbt_2.8.1 0.10.0 ...
:: retrieving :: org.scala-tools.sbt#boot-app
        confs: [default]
        34 artifacts copied, 0 already retrieved (6012kB/344ms)
[info] Set current project to default (in build file:/data/DEV/ScalaScalatraSalatMongoDB/project/plugins/)
[info] Updating...
[info] Done updating.
Getting Scala 2.9.0-1 ...
:: retrieving :: org.scala-tools.sbt#boot-scala
        confs: [default]
        4 artifacts copied, 0 already retrieved (20447kB/278ms)
[info] Set current project to default (in build file:/data/DEV/ScalaScalatraSalatMongoDB/)
> 

Tout semble bon, sbt a trouvé toutes ses dépendances, et l’on peut maintenant lancer le serveur Jetty de test :

> jetty-run

Les sources vont être compilées et un déployées sur un serveur jetty accessible à l’adresse suivante : http://localhost:8080

La page d’accueil est pilotée par ScalaScalatraSalatMongoDB/src/main/scala/fr/quidquid/rest/SSMBaseFilter.scala, via cette méthode :

get("/") {
  <html>
    <body>
      <h1>Hello tarie! (non ne pleurez pas, j'en ai des bien pires...)</h1>
      Say <a href="hello-scalate">Bonjour Quidquid !</a>.
    </body>
  </html>
}

Comme vous pouvez le voir la méthode get(“/”) écoute tous les appels à http://localhost:8080/ et répond directement en HTML (il est possible d’utiliser autre chose que du HTML en passant par des template scaml ou autres).

Maintenant pour que cela fonctionne, regardons le fichier ScalaScalatraSalatMongoDB/src/main/webapp/WEB-INF/web.xml qui déclare le filtre scalatra SSM_base :

<filter>
  <filter-name>SSM_base</filter-name>
  <filter-class>fr.quidquid.rest.SSMBaseFilte</filter-class>
</filter>
<filter-mapping>
  <filter-name>SSM_base</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

Dans ce fichier web.xml, nous déclarons aussi un autre filtre scalatra SSM_user :

<filter>
  <filter-name>SSM_user</filter-name>
  <filter-class>fr.quidquid.rest.SSMUserFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>SSM_user</filter-name>
  <url-patter>/api/user/*</url-pattern>
</filter-mapping>

Ce filtre écoute toutes les requêtes des urls type /api/user/*, qui est notre API de services REST.

De la même façon nous avons un filtre SSM_user_doc, qui écoute les requêtes de type /doc/user/* ce qui nous permettra d’afficher la documentation.

Donc afin d’ajouter un utilisateur dans MongoDB il nous faut appeller le service /api/user/add.
Pour savoir comment faire, regardons sa documentation :
http://localhost:8080/doc/user/add

Maintenant que l’on connait les attentes de ce service, ajoutons un utilisateur :
http://localhost:8080/api/user/add/moncourriel@monprovider.trucmuche/julien/ChUtcHuT

Vous pouvez obtenir depuis la console SBT l’ID généré par MongoDB de l’utilisateur (4e30622644aefeb51e57b6a4 par exemple).

Maintenant, utilisons le service http://localhost:8080/doc/user/get pour retrouver notre utilisateur :
http://localhost:8080/api/user/get/4e30622644aefeb51e57b6a4

Pour supprimer cet utilisateur, lisons la documentation de l’API ici :
http://localhost:8080/doc/user/delete

par exemple :
http://localhost:8080/api/user/delete/4e30622644aefeb51e57b6a4

Comment ça marche
Les sources sont disponibles dans le répertoire : ScalaScalatraSalatMongoDB/src/main/scala/.
J’ai 3 package principaux :
fr.quidquid.rest
Toutes les interfaces REST pour répondre aux différentes URL appelées et qui retournent des données au format JSON (a des fins de test je retourne ces données en mime type text/html afin de simplifier le debugging depuis un butineur, vous pouvez changer le comportement dans la méthode beforeSome dans la classe scala SSMBaseFilter.
Un sous-package fr.quidquid.rest.doc expose les redirections vers les templates SCAML en ce qui concerne les docs des APIs : ScalaScalatraSalatMongoDB/src/main/webapp/WEB-INF/scalate/apidocs/ (cela peut facilement devenir dynamique mais ce n’est pas l’objet de ce post).

– fr.quidquid.services
Une couche de service ridiculement simpliste qui s’occupe de faire communiquer le front et la base de données.

– fr.quidquid.domain
La couche du domaine qui contient les DAO et le modèle.

Maintenant regardons ce qui se passe en détail

ScalaScalatraSalatMongoDB/src/main/webapp/WEB-INF/web.xml défini le filtre SSM_user filter et lorsque l’URL /api/user/add est appelée, scalatra gère tout la magiplexité et nous arrivons sur la méthode fr/quidquid/rest/SSMUserFilter.get(“/api/user/add/:email/:login/:password”).
Comme vous pouvez le voir dans le code, accéder à l’un des paramètres de l’appel est trèèès simple :

params( "login" )

Cette méthode récupère les 3 paramètres depuis la requête et appelle la méhode fr/quidquid/services/UserService.add( login:String, password:String, email:String ):User (Oui je sais, cette méthode retourne directement un object du modèle, mais rien ne vous empêche de faire les choses proprement et d’ajouter vo VO, DTO ou DO…, ce tuto ne s’occupe pas de cette problématique…).

Le service instancie un User et utilise le très simple DAO (merci Salat) afin de le sauvegarder en base.

val obj = new User( login = login, password = password, email = email )
UserDao.insert(obj)

Le DAO se trouve dans cette classe : fr/quidquid/domain/mongo/User.scala

object UserDao extends SalatDAO[ User , ObjectId ](collection = MongoConnection()("quidquid")("user"))

Puis le service récupère l’object User rempli :

UserDao.findOneByID(id.get).get

et le retourne à la couche rest où l’on trasforme l’objet en notation JSON (merci jerkson) :

Json.generate(obj)

A vous maintenant

Avant de jouer avec le code et de changer plein de choses, essayer de taper votre console sbt :

> ~prepare-webapp

afin de compiler et déployer dans Jetty dès que vous faites une modification dans le code source ! Cool non ?

A vous d’ajouter vos API dans la noosphère…
@+

Previous
CentOS 6 – Install Voldemort
Next
Scala – Scalatra – Salat – MongoDB : test drive

Leave a comment

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload the CAPTCHA.