Accueil > Non classé > JEE 6 en Scala avec maven et glassfish v3, partie 1 : Servlet 3.0

JEE 6 en Scala avec maven et glassfish v3, partie 1 : Servlet 3.0

Introduction

Cet article est le premier d’une série visant à explorer les nouveautés de JEE 6, en scala. Le but de cet article est d’écrire une servlet en scala en utilisant l’annotation @WebServlet disponible depuis JEE 6.
Pourquoi ces articles ? Tout internet parle de JEE 6, ce n’est pas très original. Et il n’est pas non plus difficile de trouver des informations sur Scala.

Oui mais, les deux ensemble, c’est moins courant. Et j’avais de toute façon envie d’expérimenter cette association, et tant qu’à faire autant en faire profiter l’univers.

JEE 6 est sorti en décembre. Il inclut (entre autres) JSF 2, Servlet 3 (voir à propos de ces deux spécifications le compte rendu du paris JUG du 15 octobre disponible sur ce blog), EJB 3.1 (amélioration des EJB 3), CDI alias Weld ou web beans (API/framework d’injection de dépendances et de gestion de la vie des beans géré par le conteneur, inpiré de seam), JPA2 (amélioration de la célèbre API de persistance), bean validation (permet de valider des beans à partir d’annotations dans toutes les couches de l’application, idée ingénieuse et très pratique), et permet pour la première fois d’utiliser des EJB (light) dans un war.

Scala est un langage de programmation qui, comme groovy, jython, jruby et tant d’autres, se compile en bytecode java et s’exécute sur la JVM. Il a été créé par Martin Odersky qui a également développé les generics pour Java 5.

Scala est  33e dans l’index de Tiobe sur la popularité des langages de programmation (de mémoire il était 35e en novembre), loin donc derrière Java (number one !) ou Ruby (10e) mais loin devant groovy (44e) ou haskell (43e). Scala est assez apprécié par les grands noms de la communauté java comme James Gosling, créateur de java qui le cite comme second langage préféré sur la JVM ou encore James Strachan, créateur de groovy, qui est assez clair :

I can honestly say if someone had shown me the Programming in Scala book […] back in 2003 I’d probably have never created Groovy.

Ou pour les anglophobes :

Je peux dire honnêtement que si quelqu’un m’avait montré le livre “Programmer en scala” en 2003 je n’aurais probablement jamais créé Groovy.

Préliminaires

Installer Glassfish

Glassfish v3 est l’implémentation de référence de JEE 6, édité par Sun. Il est disponible sur cette page.

Pour l’installer, il suffit de décompresser l’archive dans le répertoire de votre choix.

Pour le démarrer il faut lancer la commande :


1
asadmin start-domain

l’exécutable asadmin se trouvant dans le répertoire bin.

On peut ensuite aller à l’adresse traditionnelle http://localhost:8080/ pour voir la page d’accueil.

Capture d’écran 2009-12-24 à 15.04.29

Notez qu’il existe également un plugin eclipse qui se télécharge simplement en allant dans la vue serveur, en faisant un new -> server et en cliquant sur le lien proposant de télécharger des adaptateurs supplémentaires.

Capture d’écran 2009-12-24 à 15.08.20

Installer Maven et l’utiliser pour créer le projet

L’installation de Maven, se fait simplement en décompressant la distribution et en ajoutant le répertoire bin dans le path.

On peut ensuite créer un projet web :


1
mvn archetype:create -DgroupId=com.exemple -DartifactId=ScalaWeb -DarchetypeArtifactId=maven-archetype-webapp

Support de Scala dans Eclipse

Il existe un plugin eclipse pour scala. La version 2.7.7 ne fonctionne pas vraiment sur mon mac, je conseille donc d’utiliser l’update site avec les builds nocturnes :  http://www.scala-lang.org/scala-eclipse-plugin-nightly, mais ce n’est pas non plus super stable.

Les vrais geeks utiliseront le support de scala dans vim, ce tutoriel fonctionne bien.

Transformer le projet en projet scala

Commencez par supprimer les éventuelles classes générées par maven et créez un répertoire src/main/scala où seront placées les sources scala.

Il faut ensuite expliquer à maven tout ce qu’il doit faire. Comme d’habitude  ça se fait dans le pom.xml et ça prend des dizaines de lignes :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.exemple</groupId>
    <artifactId>ScalaWeb</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>ScalaWeb</name>
    <dependencies>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>2.7.7</version>
       </dependency>
       <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
       </dependency>
    </dependencies>
    <repositories>
        <repository>
            <id>scala-tools.org</id>
            <name>Scala-tools Maven2 Repository</name>
            <url>http://scala-tools.org/repo-releases</url>
        </repository>
        <repository>
            <id>java.net</id>
            <url>http://download.java.net/maven/2</url>
        </repository>
    </repositories>
   <pluginRepositories>
        <pluginRepository>
            <id>scala-tools.org</id>
            <name>Scala-tools Maven2 Repository</name>
            <url>http://scala-tools.org/repo-releases</url>
        </pluginRepository>
   </pluginRepositories>
   <build>
        <finalName>ScalaWeb</finalName>
        <sourceDirectory>src/main/scala</sourceDirectory>
        <testSourceDirectory>src/test/scala</testSourceDirectory>
        <plugins>
            <plugin>
                <groupId>org.scala-tools</groupId>
                <artifactId>maven-scala-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
     </build>
</project>
  • On a besoin en dépendance de l’api scala qui contient toutes les classes et les traits de l’API scala standard. Cela se présente sous la forme d’un jar, et donc d’une dépendance maven toute simple.
  • On a également besoin, au moment de la compilation, de l’API JEE 6 (puisqu’on fait du JEE 6). Comme celle-ci est disponible directement dans glassfish, on peut utiliser le scope provided.
  • Les jars cités ci-dessus ne sont pas disponibles dans maven central. On ajoute donc deux repository : scala-tools.org et java.net.
  • Il faut configurer le build pour que maven aille chercher les sources dans src/main/scala et utilise le maven-scala-plugin pour les compiler.

La servlet

L’API servlet 3.0 rend inutile le descripteur de déploiement (fichier web.xml), si on configure tous les mapping avec des annotations et qu’on n’ajoute pas d’outil externe qui réclame une configuration dans ce fichier. On peut donc ici le supprimer.

Le conteneur scanne le classpath à la recherche de classes portant l’annotation @WebServlet. Le mapping est spécifié via l’attribut urlPatterns, ou via l’attribut value pour lequel il est inutile de préciser le nom.

Exemple en java :


1
2
3
4
@WebServlet( urlPatterns = { "/hello6.html" })
public class Hello6 extends HttpServlet {
    // ...
}

En scala une servlet Hello World s’écrit donc  :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.exemple.scalaweb

import javax.servlet.ServletException
import javax.servlet.http.HttpServlet
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import javax.servlet.annotation.WebServlet

// On passe en paramètre de l'annotation un tableau de chaines de caractères avec les mappings
@WebServlet(Array("/hello.html"))
class HelloServlet extends HttpServlet {

    override def doGet(request : HttpServletRequest,
                       response : HttpServletResponse ) {
        val html = "<html><body><h1>Hello World</h1></body></html>"
        response.getOutputStream().println(html)
    }
}

Ce code est à placer dans un fichier HelloServlet.scala.

On peut remarquer que la syntaxe est assez proche de celle de java : les imports et la déclaration de la classe sont similaires. Comme les autres langages à la mode, scala se débarrasse des archaïsmes les plus évidents de java comme les points virgules. Les classes et les méthodes sont publiques par défaut , ce qui rend le code moins verbeux.

On constate également des différences majeures : pour les paramètres de méthodes (ainsi que pour les paramètres de classes, attributs et variables locales, mais cet exemple ne le montre pas), le type est placé après le nom, avec un “:”. On remarque aussi l’utilisation du mot clef def pour les méthodes ainsi que l’utilisation de val et l’absence de déclaration de type de la variable locale html. val signifie que la variable ne peut prendre qu’une seule valeur. C’est l’équivalent d’une variable locale finale en java. Pour une variable non finale  il faut utiliser le mot clef var.

Il faut savoir que Scala est un langage alliant le paradigme de programmation impératif (comme on a l’habitude en C ou en Java), et le paradigme fonctionnel (comme en lisp par exemple), que les concepteurs du langage encouragent. Les programmes écrits dans des langages utilisant ce paradigme étant plus “purs” et plus succins.Concrètement cela implique des objets non modifiables ( immutable), des “constantes locales” comme html ci dessus, et l’utilisation des fonctions sous toutes les formes imaginables (fonctions locales, closures, …).

Les habitués des langages à typage dynamique comme flex, javascript ou groovy penseront retrouver là une syntaxe qui leur est familière. Il y a cependant une grande différence :

En groovy on peut écrire :


1
2
def truc = "toto"
truc = 3.0

En scala cela ne compile pas. Scala est un langage à typage statique, ce qui signifie que le type d’une variable est fixé à la compilation. Scala utilise l’inférence de type : le type de la variable est déduit du contexte, ici on lui affecte une chaine de caractère, le compilateur en déduit donc que html est une variable de type chaine de caractère.

On a enfin l’utilisation du mot clef “override” pour spécifier que la méthode redéfinit la méthode de même nom de la super classe. C’est le même principe, en plus strict, que le @Override que l’on utilise depuis Java 5.

On peut maitenant lancer un build maven :

mvn install

et copier le war résultant dans le répertoire autodeploy de glassfish :

cp target/ScalaWeb.war ${glassfishbasedir}/glassfish/domains/domain1/autodeploy/

une fois le serveur on peut constater que la servlet fonctionne :

Capture d’écran 2009-12-28 à 12.27.12

Étoffons la maintenant pour afficher les headers http dans un tableau :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package com.exemple.scalaweb

import javax.servlet.ServletException

import javax.servlet.http.HttpServlet
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import javax.servlet.annotation.WebServlet

@WebServlet(Array("/hello.html"))
class HelloServlet extends HttpServlet {

   override def doGet(request : HttpServletRequest,
                                response : HttpServletResponse ) {
       // fonction locale : n'est définie qu'au sein de doGet()
       implicit def enumerationToList[A](e : java.util.Enumeration[A]) : List[A] = {
            var finalList : List[A] = List()
            while (e.hasMoreElements()) {
                // "::" est l'opérateur permettant d'ajouter un élément à la liste
                finalList = e.nextElement() :: finalList
            }
            return finalList
       }

       // début du code de la méthode doGet
       // Utilisation d'une chaine de caractère multilignes
       var html = """
           <!DOCTYPE html>
           <html>
               <head>
                   <link rel="
stylesheet" type="text/css" href="style.css" />
               </head>
               <body>
                   <h1>Hello, world</h1>
                   <h2>Http headers<h2>
                   <table>
                       <thead>
                           <tr>
                               <th>Header name</th>
                               <th> Header value</th>
                           </tr> </thead>
                 <tbody>"
""

        // Cast et conversion implicite via enumerationToList
        // headerNames est de type List[String]
        val headerNames = request.getHeaderNames().asInstanceOf[java.util.Enumeration[String]]

        // parcours de la liste
        headerNames.foreach {
            // la closure concatène une ligne de tableau au html
            (headerName) =>
                html += "<tr><td>"
                           + headerName
                           + " </td><td>"
                           + request.getHeader(headerName)
                           + "</td></tr>"
       }

       html += "</tbody></table></body></html>"
       response.getOutputStream().println(html)
   }
}

On initialise maintenant le html avec une chaine de caractère de plusieurs lignes. Elle commence et se termine par trois quotes. On peut écrire d’autres quotes (ainsi que tous les caractères spéciaux comme “\”) à l’intérieur sans les échapper.

On écrit également une fonction locale enumerationToList permettant de convertir implicitement une instance de java.util.Enumeration en liste scala. C’est l’utilisation du mot clef “implicit” qui rend l’appel à la méthode transparent : il est inutile de l’utiliser explicitement. En revanche, comme scala ne permet pas d’avoir des listes non typées, il faut caster l’énumération en énumération de chaine de caractères, ce qui se fait avec asInstanceOf.

Enfin avec notre liste on peut utiliser la méthode foreach qui prend un bout de code en paramètre, concaténant une ligne de tableau au html. Précisons pour les puristes qu’il s’agit ici bien d’une closure  puisqu’on modifie une variable locale.

Voici le résultat :

Capture d’écran 2009-12-28 à 14.52.33

Conclusion

Ce premier article nous a permis de mettre en place un environnement, et de tester (sommairement) l’API servlet 3.0 en déployant une servlet annotée et sans descripteur de déploiement, le tout en Scala, en utilisant quelques fonctionnalités intéressantes .

Le code est volontairement plus compliqué que nécessaire, on pourrait par exemple utiliser un simple while pour parcourir l’énumération et se passer de conversion implicite, de fonction locale et de closure, mais ce serait moins marrant.

Dans le prochain article nous parlerons des EJB 3.1 et de Web profile.

Share
Categories: Non classé Tags: , , , ,
  1. 29/01/2017 à 02:43 | #1

    バイクメーカーが過去のバイクをモチーフに新モデルを開発するのは最近では珍しいことではないが、そういった場合は196070年代の車両を参考にすることがほとんど。

  2. 04/02/2017 à 10:02 | #2

    性描写はほどんどないのですが逆にそんなのなくても凄く伝わってくる人としての繋がり的なものを感じました。

  3. 06/03/2017 à 02:57 | #3

    汗をかいてもさらさらとした着心地が持続し、綿のシャツのように「アウター」として着用していただけます。

  1. 04/02/2010 à 10:42 | #1
  2. 17/06/2014 à 06:09 | #2