Libellés

jeudi 21 juillet 2011

Ecrire un DSL en Groovy

Récemment j'ai écrit un DSL en Groovy pour WRO4J, et je vais vous expliquer comment y arriver soi même.

Voici à quoi ressemble au final le DSL :

groups {
g1 {
js "/static/app.js"
css "/static/app.css"
}
"an-other-group" {
js("classpath:com/application/static/app.js")
css(minimize: false, "http://www.site.com/static/app.css")
}
}

https://github.com/Filirom1/wro4j/blob/master/wro4j-extensions/src/test/resources/ro/isdc/wro/extensions/model/factory/Wro.groovy

Si l'on analyse un plus le DSL, on retrouve :

- une closure groups englobant l'ensemble du dsl
- deux closures g1 et an-other-group ayant des noms personnalisées
- deux methodes js et css, pouvant prendre 1 ou deux arguments : js(Map params = [:], String uri)


Maintenant pour construire ce DSL nous allons partir de la fin, les méthodes js et css.
Voici le code source, regarder la signature des méthodes :

class ResourceDelegate {
List resources = new ArrayList()

void css(Map params = [:], String name) {
def resource = Resource.create(name, ResourceType.CSS)
if (params.get("minimize") == false) resource.minimize = false
resources.add(resource)
}

void js(Map params = [:], String name) {
def resource = Resource.create(name, ResourceType.JS)
if (params.get("minimize") == false) resource.minimize = false
resources.add(resource)
}
}

Le premier paramètre params est facultatif. Nous pouvons donc utiliser ces méthodes comme ceci :

def resourceDelegate = new ResourceDelegate()

//avec params
resourceDelegate.js([minimize: true, key: value], "js/script.js")
resourceDelegate.js(minimize: true, key: value, "js/script.js")

//et sans params
resourceDelegate.js([:], "js/script.js")
resourceDelegate.js("js/script.js")
resourceDelegate.js "js/script.js"


Maintenant il nous faut intégrer ces méthodes dans une closure pour pouvoir écrire un DSL comme celui là :

myGroup{
js "script.js"
js(minimized: true, "script2.js")
}


Je défini ici une méthode myGroup prenant comme seul paramètre une closure et qui exécute cette closure.

class GroupDelegate {
def myGroup(Closure cl){
cl()
}
}

Je peux donc appeler cette méthode de différentes façons :

myGroup({
println "inside the closure"
})
myGroup {
println "inside the closure"
}

Je vais choisir la deuxième façon qui est plus joli pour un DSL.

Je modifie cette closure pour déléguer tout les appels se passant à l'interieur de cette closure vers une instance de ResourceDelegate (défini dans la première partie)

class GroupDelegate {
List groups = new ArrayList()

def myGroup(Closure cl){
cl.delegate = new ResourceDelegate()
cl.resolveStrategy = Closure.DELEGATE_FIRST
cl()
groups.add(new Group(name: 'myGroup', resources: cl.resources))
}
}

Et maintenant je peux écrire :

@Test
public void testGroupDelegate() {
//setup:
def groupDelegate = new GroupDelegate()

//when:
groupDelegate.myGroup {
js("/js/script.js")
css("/css/style.css")
}

//then:
assert 1 == groupDelegate.groups.size()
assert "myGroup" == groupDelegate.groups[0].name
assert 2 == groupDelegate.groups[0].resources.size()
}


Notre DSL commence à prendre forme, mais nous ne pouvons créer qu'un seul groupe. L'idée maintenant serait de pouvoir écrire plusieurs groupes comme ci dessous :

groupDelegate.g1 {
js("/js/script.js")
css("/css/style.css")
}
groupDelegate.'second-group' {
js("/js/script.js")
css("/css/style.css")
}


Rien de plus simple en groovy, il suffit d'utiliser la méthode def methodMissing(String name, args).
La méthode methodMissing est appelée quand groovy ne trouve aucune méthode correspondant. La variable name nous donne le nom de la méthode appelée et args la liste des arguments passés à la méthode.

Voici cette fois la classe GroupDelegate utilisant methodMissing :

class GroupDelegate {
List groups = new ArrayList()

def methodMissing(String name, args) {
def cl = args[0]
cl.delegate = new ResourceDelegate()
cl.resolveStrategy = Closure.DELEGATE_FIRST
cl()
groups.add(new Group(name: name, resources: cl.resources))
}
}


On approche de la fin, maintenant il nous faut intégrer les différents groupes à l'intérieur d'une closure comme ceci :

wroModelDelegate.groups {
g1 {
js("/js/script.js")
css("/css/style.css")
}
'second-group' {
js("/js/script2.js")
}
}

Comme nous avons déjà réalisé cette tâche précédemment, je vous donne directement la solution (il faut déléguer les appels se passant à l’intérieur de la closure vers une instance de GroupDelegate) :

class WroModelDelegate {
WroModel wroModel = new WroModel()

void groups(Closure cl) {
cl.delegate = new GroupDelegate()
cl.resolveStrategy = Closure.DELEGATE_FIRST
cl()
wroModel = new WroModel(groups: (Collection) cl.getProperty("groups"))
}

}

Courage on approche de la fin. Maintenant on aimerait pouvoir écrire notre DSL dans un fichier dsl.groovy, parser ce fichier et récupérer la valeur.
Pour cela nous allons travailler avec la classe Script de groovy.

def dsl = """
groups {
g1 {
js("/js/script.js")
css("/css/style.css")
}
g2 {
js("/js/script2.js")
}
}
"""

Script dslScript = new GroovyShell().parse(dsl)
dslScript.metaClass.mixin(WroModelDelegate)
dslScript.run()

Ce script (dslScript) contient une liste d'instruction dont la première est d'appeler la methode groups(Closure). Sauf que cette méthode n'est pas connu naturellement dans dslScript.
C'est pour cela que nous faisons appel à dslScript.metaClass.mixin(WroModelDelegate).
Nous mixons le script avec WroModelDelegate, ce qui correspond à ajouter toute les méthodes et les attributs de WroModelDelegate dans le dslScript.

Maintenant il suffit d'executer le script et c'est gagné :D
dslScript.run()



L'ensemble du code source se trouve ici : https://github.com/Filirom1/wro4j/blob/master/wro4j-extensions/src/main/groovy/ro/isdc/wro/extensions/model/factory/GroovyWroModelParser.groovy

Et les tests là :
https://github.com/Filirom1/wro4j/blob/master/wro4j-extensions/src/test/groovy/ro/isdc/wro/extensions/model/factory/TestGroovyWroModelParser.groovy


Une version testable et modifiable en ligne est également disponible ici :
http://groovyconsole.appspot.com/script/516002

Amusez vous bien !!!

PS : je me suis fortement inspiré de http://groovy.dzone.com/news/groovy-dsl-scratch-2-hours

GroovyClassLoader charger et exécuter un Script

Lorsque l'on travaille avec Groovy il est assez facile de charger une classe avec GroovyClassLoader#loadClass

Mais comment faire pour charger et exécuter un script comme celui ci :

[romain@dahu tmp]$ cat MyScript.groovy
println ">>>>>>>>>>>>>>>> It works"
println 10 + 2


En fait c'est assez simple, il suffit de charger la classe, de l'instancier puis de la caster en Script.

[romain@dahu tmp]$ ls
MyScript.groovy Runner.groovy


[romain@dahu tmp]$ cat Runner.groovy
def clazz = this.getClass().getClassLoader().loadClass("MyScript")
def script = (Script) clazz.newInstance()
script.run()


Et ça fonctionne :

[romain@dahu tmp]$ groovy Runner.groovy
>>>>>>>>>>>>>>>> It works
12

lundi 18 juillet 2011

Xrandr forcer la résolution de son écran

J'ai acheté un écran : Packard Bell Maestro 221W qui est sensé supporter la résolution 1680x1050 mais Linux ne la reconnait pas.

Mais grace à xrandr, nous pouvons forcer la résolution.

Sans parametre xrandr affiche les résolutions disponibles : 
[romain@dahu ~]$ xrandr 
Screen 0: minimum 320 x 200, current 1024 x 768, maximum 8192 x 8192
DVI-I-1 disconnected (normal left inverted right x axis y axis)
DVI-I-2 connected 1024x768+0+0 (normal left inverted right x axis y axis) 0mm x 0mm
   1024x768       60.0* 
   800x600        60.3     56.2  
   848x480        60.0  
   640x480        59.9

Nous voyons bien ma résolution 1680x1050 n'est pas reconnue.
Ma carte graphique a deux sorties DVI,  et nous remarquons que c'est la sortie DVI-I-2 qui est utilisée . Important pour la suite.

Je vais maintenant ajouter le mode 1680x1050.

[romain@dahu ~]$ xrandr --newmode "1680x1050" 146.25 1680 1784 1960 2240 1050 1053 1059 1089 -hsync +vsync

Mais comment connître tout ces paramètres lorque l'on ne connait que la résolution de son écran ??? En fait c'est assez simple, il suffit d'utiliser la commande cvt :

[romain@dahu ~]$ cvt 1680 1050
# 1680x1050 59.95 Hz (CVT 1.76MA) hsync: 65.29 kHz; pclk: 146.25 MHz
Modeline "1680x1050_60.00"  146.25  1680 1784 1960 2240  1050 1053 1059 1089 -hsync +vsync

Il suffit de copier coller les paramètres à la suite de 
xrandr --newmode "1680x1050" ......

Maintenant nous allons associer ce mode, à la sortie de notre carte graphique. Nous avions repéré précédemment que nous utilisions la sortie DVI-I-2.
[romain@dahu ~]$ xrandr --addmode DVI-I-2 "1680x1050"

Et maintenant nous allons utiliser ce mode
[romain@dahu ~]$ xrandr -s 1680x1050

Normalement c'est gagné, votre écran vient de changer de résolution.

Ces commandes seront à executer à chaque fois que vous redémarrez votre ordinateur. 

mercredi 13 juillet 2011

Benchmark d'une classe, d'une méthode, ... en Groovy

Pour mesurer le temps d'exécution d'une méthode en Java, on utilise de l'AOP ou des println à la main.

EN Groovy, je viens de découvrir un projet qui s'appelle GBench qui permet de mesurer des temps d’exécutions.

On peut mesurer les temps d’exécutions de toutes les méthodes d'une classe :


@Grab(group='com.googlecode.gbench', module='gbench', version='11.07.05')
import gbench.*

@Benchmark()
class Klass {
def foo() {
Thread.sleep(10)
}
def bar() {
Thread.sleep(5)
}
}

def k = new Klass()

println "call foo"
k.foo()

println "call bar"
k.bar()

Klass void (): 14999053 ns
call foo
Klass java.lang.Object foo(): 14887245 ns
call bar
Klass java.lang.Object bar(): 5129355 ns



Seulement d'une méthode (et regarder comment spécifier le format d'affichage) :

@Grab(group='com.googlecode.gbench', module='gbench', version='11.07.05')
import gbench.*

class Klass {

@Benchmark({println("${method} of ${klass}: ${(time/1000000) as long} ms")})
def foo() {
Thread.sleep(10)
}
def bar() {
Thread.sleep(5)
}
}

def k = new Klass()

println "call foo"
k.foo()

println "call bar"
k.bar()


call foo
java.lang.Object foo() of Klass: 14 ms
call bar



Ou faire des comparaisons sur 1000 exécutions.
Dans cet exemple je compare le Parseur JSON de Groovy 1.8 avec celui de Jackson :


@Grab(group='com.googlecode.gbench', module='gbench', version='11.07.05')
@Grab(group='org.codehaus.jackson', module='jackson-mapper-asl', version='1.8.3')

import gbench.*
import groovy.json.JsonSlurper
import org.codehaus.jackson.map.ObjectMapper;

def slurper = new JsonSlurper()
def mapper = new ObjectMapper();

def jsonString = '{"name": "Romain", "age": 25}'
class Personn{ String name; int age }

def bench = new BenchmarkBuilder()
bench.run(times: 1000){
with 'Groovy JSON', {
Personn p = slurper.parseText(jsonString)
assert p.name == "Romain"
assert p.age == 25
}
with 'Jackson', {
Personn p = mapper.readValue(jsonString, Personn.class)
assert p.name == "Romain"
assert p.age == 25
}
}

println bench


time
Groovy JSON 179218175
Jackson 66699800

Singleton en Groovy

Rien de plus simple que de faire un Singleton en Groovy. Cela se résume à annoter une classe par ​@Singleton, puis à utiliser l'instance via le getter getInstance()

Je vous donne un exemple, testable ici:


​@Singleton
class Personn {
String name
int age
}

​def ​p = Personn.instance;
​p.name = "Romain"
p.age = 25

assert p == Personn.instance;



Si jamais vous tentez d'utiliser le constructeur par défaut, une exception sera lancée.


java.lang.RuntimeException: Can't instantiate singleton Personn. Use Personn.instance
at Personn.(Script1.groovy)
at Script1.run(Script1.groovy:9)




Par contre si vous créez un constructeur (même privé), ça ne marche plus.


@Singleton class Personn {

private Personn(){
println "toto"
}

String name

int age
}

def p1 = new Personn();
def p2 = new Personn();

assert p1 == p2

Assertion failed:

assert p1 == p2
| | |
| | Personn@1ee0563
| false
Personn@1a3e311

jeudi 7 juillet 2011

UglifyJs en Java

UglifyJS est une librairie javascript qui permet de minifier des fichiers javascripts.

Normalement UglifyJs est utilisé avec Node.js, mais si vous voulez appeler UglifyJs depuis un environnement Java vous pouvez le faire à l'aide du projet Wro4J

Vous allez surement me demander pourquoi utiliser UglifyJS en Java alors que Google Closure répond exactement à ce problème ? Vous auriez raison, mais c'est tellement plus drôle d'essayer de faire tourner du Javascript dans une JVM !!!

Aller je vous un bout de code codé en Groovy qui utilise UglifyJS :


@Grab(group = 'ro.isdc.wro4j', module = 'wro4j-extensions', version = '1.3.8')

import ro.isdc.wro.extensions.processor.algorithm.uglify.*

def uglify = UglifyJs.uglifyJs()
uglify.process(new File("/home/romain/plugin/web/web-app/lib/route.js").text);


Wro4J utilise Rhino pour exécuter le Javascript.
Les sources sont ici :
https://github.com/alexo/wro4j/blob/master/wro4j-extensions/src/main/java/ro/isdc/wro/extensions/processor/algorithm/uglify/UglifyJs.java

mardi 5 juillet 2011

Maven : créer un projet multi-module

Maven, le très célèbre outil de build et gestionnaire de dépendances nous offre quelques possibilités bien sympatique.

Vous connaissez sûrement les projets multi-modules, qui permettent de découper un projet pour plus de modularité.

Par exemple vous construisez un projet P. Ce projet est constitué de plusieurs sous-projet A, B, C ayant comme dépendances respectives :

P :
  - A (A1, O)
  - B (O)
  - C (C1, A)

Comme vous le voyez, C dépend de A, mais pas de B. De plus la dépendance O est partagée entre plusieurs sous-projet.

Maven va bous permettre de gérer ses trois sous projet en réduisant le nombre de lignes de code copiées collées.



Voici le pom de P, le projet coordinateur :

<project ....>

    <groupId>org.groupId</groupId>
    <artifactId>P</artifactId>
    <packaging>pom</packaging>
    <version>1.0.0</version>
    <modelVersion>4.0.0</modelVersion>
    
    <modules>
        <module>A</module>
        <module>B</module>
        <module>C</module>
    </modules>
</project>


Il faut remarquer deux choses :
 
  - le packaging == pom, qui nous dit que ce projet n'aura pas de code source
  - la section modules qui représente notre structure.


Ensuite à la racine de ce projet, nous allons créer les 3 sous projets : A, B, C

/my-project
|--pom.xml
|--A
|  `--pom.xml
|--B
|  `--pom.xml
`--C
   `--pom.xml     



Voici le pom du sous-projet A  :

<project ...>
    <parent>
        <artifactId>P</artifactId>
        <groupId>org.groupId</groupId>
        <version>1.0.0</version>
    </parent>
    
    <modelVersion>4.0.0</modelVersion>
    <artifactId>A</artifactId>
    
....


Dans la balise parent, on défini la dépendance vers notre projet parent P.

Nous remarquons que pour A, il n'y a pas de balise groupId ni de balise version à définir A.
En effet, ces valeurs sont héritées du pom parent, pratique !!!!



Maintenant prenons le cas où certains sous-projets aient des dépendances en commun. A et B dépendent de O.

On pourrait tout a fait déclarer dans A et dans B les dépendances complètes (avec la version) :
<dependencies>
    <dependency>
        <groupId>com.aProject</groupId>
        <artifactId>O</artifactId>
        <version>5.6.7</version>
    </dependency>
</dependencies>


Mais le jours, on l'on changera la version de O ( 5.6.7 ---> 5.6.8 ) dans A, on oubliera sûrement de changer la version de O dans B, et l'on se retrouvera avec un pom dégelasse avec plein de dépendances différentes.

La solution consiste donc à ne pas déclarer les versions dans les poms des sous-projets, mais dans le pom parent.


Je vous donne un exemple :

<project ....>

    <groupId>org.groupId</groupId>
    <artifactId>P</artifactId>
    <packaging>pom</packaging>
    <version>1.0.0</version>
    <modelVersion>4.0.0</modelVersion>
    
    <modules>
        <module>A</module>
        <module>B</module>
        <module>C</module>
    </modules>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>2.0.1</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.8.2</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>




Dans le pom d'un sous-projet, la section de dépendance ressemblera à :
<dependencies>
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

Cool, on a supprimé la version !!!!



Maintenant voyons le cas où C dépend de A, c'est à dire étudions les dépendances entre sous-projets.

Voici la section de dépendance de C :
<dependencies>
    ....
    <dependency>
        <groupId>${parent.groupId}</groupId>
        <artifactId>A</artifactId>
        <version>${parent.version}</version>
    </dependency>
    ....
</dependencies>


On est obligé de spécifier la version, mais regardez bien, nous faisons référence à une variable interne : ${parent.version}, de même pour la balise groupId. Zéro problème pour maintenir ce pom !!!


Et maintenant nous pourrions avoir envie de créer nos propres variables dans le pom parent et d'utiliser n'importe où.
Aussitôt dit, aussitôt fait. Editez votre pom parent et ajoutez la section :
<properties>
    <foo.bar>FooBarValue</foo.bar>
</properties>

Maintenant on peut utiliser cette valeur de partout ce cette façon : ${foo.bar}

Il y a bien sur une limite, la section parent ne peut être défini à partir de variable défini dans le pom parent. Sinon ce n'est pas évident pour deviner qui est le parent quand on lit le pom d'un sous-projet.