Libellés

mercredi 13 juillet 2011

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.

mardi 28 juin 2011

Compiler des fichers less -> css en Groovy

Less-css est un CSS amélioré.

Pour citer quelques exemples on peut :
- créer des variables et les réutiliser partout
- imbriquer des blocs css, dans d'autre bloc css.
- ...

Par exemple :

@import "global.less";

@size: 14px;

#col{

.odd{
color: red;
font-size: @size;
}

.even{
color: blue;
font-size: @size;
}

}


Les fichiers less sont transformés en CSS par un script js. Ce qui permet de tester en live sur son site web, mais la conversion less/css est plutôt lente.

Il est donc préférable de compiler les fichiers less avant la prod.

Je vous livre donc un code groovy qui permet très facilement de convertir un fichier less en CSS. A vous de le transformer en JAVA et de l'intégrer dans votre environnement de développement.

Pour info, il existe lessc, le compilateur less officiel qui repose sur nodeJS. Si vous ne voulez pas installer node.js, cette version Java vous plaira (elle utilise Rhino).


@Grab(group='com.asual.lesscss', module='lesscss-engine', version='1.0.43')
@GrabResolver(name='asual', root='http://www.asual.com/maven/content/repositories/releases')

import com.asual.lesscss.*

new LessEngine().compile(new File("/home/romain/Bureau/less/root.less"),new File("/home/romain/Bureau/less/root.css"));

lundi 27 juin 2011

Groovy et l'immutabilité

Depuis la version 1.8, Groovy permet de rendre des classes entièrement immutables, c'est à dire des classes qui ne peuvent pas changer d'état.

Ceci peut être pratique pour écrire du code thread safe.

Voici un exemple de classe immutable en Groovy.
PS : ne surtout pas oublier le import groovy.transform.Immutable, sinon c'est l'ancien groovy.lang.Immutable qui est utilisé (Cf : http://comments.gmane.org/gmane.comp.lang.groovy.user/55021)


import groovy.transform.Immutable

@Immutable
final class Punter {
String first
String last
Map params
}

@Immutable
final class PunterWrapper {
String name
Punter punter
}


def wrapper = new PunterWrapper(
name: 'hi',
punter: new Punter(
first: 'Foo',
last: 'Bar',
params: [k: 'value']
)
)


println wrapper

try{
wrapper.punter.params.toto = 3
}catch(Exception e){
println('Exception catched ')
}

try{
wrapper.punter.last = 'Tutu'
}catch(Exception e){
println('Exception catched ')
}

try{
wrapper.name = 'Toto'
}catch(Exception e){
println('Exception catched ')
}

PunterWrapper(name:hi, punter:Punter(first:Foo, last:Bar, params:[k:value]))
Exception catched
Exception catched
Exception catched




Mais écrire des classes immutables en Groovy peux s'avérer complexe à gérer.

Pour éviter récrire le constructeur complet de l'objet à chaque fois que l'on souhaite modifier une valeur, voici une solution que j'ai trouvé :


println new PunterWrapper(wrapper.properties + [
name: 'Titi',
punter: new Punter(wrapper.punter.properties + [
first: 'Mickael'
])
])​

PunterWrapper(name:Titi, punter:Punter(first:Mickael, last:Bar, params:[k:value]))



Un nouvel objet est créé à partir des attributs modifiés de l'ancien objet.



On peut également travailler avec les Collections de la même façon :

def list = [a: 1,b: 2,c: 3].asImmutable()
println list

try{
list​.d=4
}catch(Exception e){
println 'Exception catched'
}


def list2 = (list + [d:4]).asImmutable()
​println list2


def listMutable = list2 + [e:5]
listMutable.f = 6
println listMutable


[a:1, b:2, c:3]
Exception catched
[a:1, b:2, c:3, d:4]
[a:1, b:2, c:3, d:4, e:5, f:6]



Mais il ne faut pas oublier le asImmutable() à chaque modification de la collection.


Groovy nous donne donc quelques possibilités pour travailler dans un environnement immutable, mais ce n'est pas encore très pratique.

Scala est mieux sur ce point là.

lundi 20 juin 2011

JAVA et Groovy

Groovy est un langage de programmation, basé sur Java et s'intégrant très bien avec tout son écosystème.

Pour vanter quelques mérites de Groovy voici des exemple Groovy VS Java :



JAVA
--------
String name = "World"
System.out.println("Hello " + name);

GROOVY
-----------
def name = "World"
println "Hello $name"


JAVA
--------
Map<String, String> params = new HashMap<String, String>();
params.put("key", "value")
System.out.println(params.get("key"))

GROOVY
-----------
def params = [key: "value"]
println params.key



JAVA
--------
public class Customer {

    private String name;

    private int age;

    Customer() {
    }

    Customer(String name, int age) {
        this.age = age;
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Customer customer = (Customer) o;

        if (age != customer.age) return false;
        if (name != null ? !name.equals(customer.name) : customer.name != null) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public static void main(String[] args) {
        Customer toto = new Customer("Toto", 7);

        Customer toto2 = new Customer();
        toto2.setName("Toto");
        toto2.setAge(7);

        System.out.println(toto.equals(toto2));
    }
}


GROOVY
-----------
import groovy.transform.* 

def toto = new Customer(name: 'Toto', age: 7)

def toto2 = new Customer()
toto2.name = 'Toto'
toto2.age = 7

assert toto == toto2

@ToString 
@EqualsAndHashCode 
class Customer{

  String name

  int 
age


}​






Comme vous le voyez, Groovy permet d'avoir une syntaxe bien plus concise que l'équivalent en Java.

Moins de code à écrire == plus facile à développer == plus facile à lire (une fois que l'on a l'habitude) == plus facile à maintenir...


Et la liste est longue des Groovyness utilisables : 
  - la gestion des collections : filtrage, trie, modification, .... (simplifié grâce à la programmation fonctionnelle) : http://groovy.codehaus.org/Collections
  - la gestion des dates : http://groovy.codehaus.org/JN0545-Dates
  - la création de script dépendant de librairies présent sur Maven : http://groovy.codehaus.org/Grape
  - éviter les null pointer exceptions avec les opérateurs ?. et ?: : http://docs.codehaus.org/display/GROOVY/Operators#Operators-ElvisOperator%28%3F%3A%29
  - la gestion des fichiers : http://groovy.codehaus.org/JN2015-Files
  - toute les exceptions sont gérées comme des RuntimeException
  - ...

Groovy est un langage qui évolue vite, actuellement nous sommes à la version 1.8. Cette version ci rajoute un certain nombre de fonctionnalités intéressantes comme le support natif du JSON, l’annotation @Log qui permet d'injecter un logger (slf4j, log4j, ...), @ToString et @EqualsAndHashCode que je viens de vous montrer et encore pleins d'autres choses merveilleuses à découvrir ici : http://docs.codehaus.org/display/GROOVY/Groovy+1.8+release+notes






Depuis plus d'un an que j'utilise groovy tous les jours, je n'ai toujours pas rencontré de bug. Nous l'avons utilisé pour des scripts, des tests et des applications en production (http://www.grails.org/). C'est un langage que je recommande vraiment. Pour un développeur Java, c'est vraiment une bouffé d'air frais.

Il n'y a pas que des avantages, Groovy est lent, mais je trouve que le temps gagné lors de la phase de développement en vaut la chandelle.

Groovy est dynamique et n'est pas fortement typé, ce qui nécessite d'écrire plus de test unitaire. C'est peut être là sont plus gros défaut. 

Néanmoins, ces problèmes disparaîtront peut être un jours car les performance de Groovy ne font que s'améliorer avec le temps. De plus groovy++ est un projet prometteur car il ajoute un typage statique et des performances équivalentes à celles de Java. Mais je ne l'ai encore jamais testé.

L'intégration avec les IDE n'est pas forcement parfaite : 
  Netbeans -> moyen, 
  IntelliJ -> excellent, équivalent à Java

Mais l'integration avec Maven ne pose aucun problème : http://docs.codehaus.org/display/GMAVEN/Building+Groovy+Projects

Concernant les tests je conseille le framework Spock, sinon un simple JUnit fait très bien l'affaire. Pour du BDD, on peut utiliser EasyB, mais je préfère quand même Spock.

Come Java, Groovy ne favorise pas l'immutabilité (contrairement à scala) est c'est bien dommage pour les applications multi-threadées.

GPars a été intégré avec Groovy depuis la version 1.8 pour simplifier la programmation multi-threadée, mais j'ai encore un peu de mal à l'utiliser. Je ne trouve pas l'API intuitive. Mais bon je vais surement m'y habitué.

Pour avoir également testé Scala quelques jours, Groovy est peut être moins puissant mais est plus facile à prendre en main lorsque l'on est habitué à Java. Mais Scala semble quand même plus robuste et plus simple pour tout ce qui concerne le développement d'applications multi-threadées, tolérantes aux pannes et scalable  : http://akka.io/

lundi 6 juin 2011

TopCoder Arena

Ce soir je vais sur le site de recrutement de Google, et dans la section Préparation de l'entretien, Google nous conseil de s’entraîner sur TopCoder Arena.

Aller je tente.

Après un processus d'enregistrement super chi..., où l'on me demande ma couleur de peau (merci les Etats-Unis) je peux enfin avoir des accès.

Top Coder Arena est une Applet Java qui affiche des problèmes informatiques à résoudre. On se croirait en cours pendant un TD d'info. L'appli permet de compiler et de tester son code avant de le soumettre.

J'ai commencé facile avec un problème à 150 points. Je vous le donne :


Problem Statement
    
***Note: Please keep programs under 7000 characters in length. Thank you


Class Name: HowEasy
Method Name: pointVal
Parameters: String
Returns: int

TopCoder has decided to automate the process of assigning problem difficulty
levels to problems. TopCoder developers have concluded that problem difficulty
is related only to the Average Word Length of Words in the problem statement:

If the Average Word Length is less than or equal to 3, the problem is a 250
point problem.
If the Average Word Length is equal to 4 or 5, the problem is a 500 point
problem.
If the Average Word Length is greater than or equal to 6, the problem is a 1000
point problem.

Definitions:
Token - a set of characters bound on either side by spaces, the beginning of
the input String parameter or the end of the input String parameter.
Word - a Token that contains only letters (a-z or A-Z) and may end with a
single period. A Word must have at least one letter.
Word Length - the number of letters in a Word. (NOTE: a period is NOT a letter)

The following are Words :
"ab", "ab."

The following are not Words :
"ab..", "a.b", ".ab", "a.b.", "a2b.", "."

Average Word Length - the sum of the Word Lengths of every Word in the problem
statement divided by the number of Words in the problem statement. The
division is integer division. If the number of Words is 0, the Average Word
Length is 0.

Implement a class HowEasy, which contains a method pointVal. The method takes
a String as a parameter that is the problem statement and returns an int that
is the point value of the problem (250, 500, or 1000). The problem statement
should be processed from left to right.

Here is the method signature (be sure your method is public):
int pointVal(String problemStatement);

problemStatement is a String containing between 1 and 50 letters, numbers,
spaces, or periods. TopCoder will ensure the input is valid.

Examples:

If problemStatement="This is a problem statement", the Average Word Length is
23/5=4, so the method should return 500.
If problemStatement="523hi.", there are no Words, so the Average Word Length is
0, and the method should return 250.
If problemStatement="Implement a class H5 which contains some method." the
Average Word Length is 38/7=5 and the method should return 500.
If problemStatement=" no9 . wor7ds he8re. hj.." the Average Word Length is 0,
and the method should return 250.
Definition
    
Class: HowEasy
Method: pointVal
Parameters: String
Returns: int
Method signature: int pointVal(String param0)
(be sure your method is public)
    


Et je vous donne ma solution.


import java.util.regex.*;

public class HowEasy{

Pattern regex = Pattern.compile("[a-zA-Z]+\\.?$");

public int pointVal(String problemStatement){
String[] words = problemStatement.split(" ");
int matchingWords = 0;
int wordLength=0;
for(String word: words){
Matcher m =regex.matcher(word);
if(m.matches()){
matchingWords++;
wordLength+=word.length();
}


}

if(matchingWords==0) return 250;

int avgWordLength = wordLength / matchingWords;
if(avgWordLength<=3){
return 250;
}else if(avgWordLength==4){
return 500;
}else if(avgWordLength==5){
return 500;
}else{
return 1000;
}


}

}


150 points / 250, c'est pas terrible, je peux mieux faire.
Je pense que j'ai oublié d'enlever les points pour calculer la longueur des mots.

A vous !!!