jeudi 23 août 2012

Comparaison des parseurs CSV

Mon petit projet "CsvEgine" commençant à ressembler à quelque chose, je vais me lancer dans un petit comparatifs des parseurs que je peux trouver sur internet.

Pour pouvoir comparer ces différents parseurs je vais utiliser l'exemple basique de l'utilisation de CsvEngine : Basics.
Il s'agit donc de parser un fichier CSV contenant des chiens avec comme attributs le nom, la race et le propriétaire.
On verra également comment écrire le fichier.
La troisième étape de comparaison sera la validation :
  • Le nom et la race sont obligatoires
  • La race doit faire partie d'une liste connues de races

La quatrième étape sera du parsing CSV complexe, ajout de retour à la ligne pour le propriétaire.

Et enfin pour finir la comparaison je finirai avec un petit bench, en reprenant ce que j'avais fait mon "CsvEngine" (qui à l'époque s'appelait MoteurCsv) : Bench MoteurCsv

Pour la liste des parseurs, je suis parti de cet article : Java et CSV tour d'horizon des solutions open-source
Je rajouterai quand même CsvEngine :)

Comme d'habitude, l'ensemble du code est disponible sur github : CsvJavaComparaison.

BeanFiles

Site : http://code.google.com/p/beanfiles/

Étape 0 : Documentation et mise en place

Voici tout d'abord les problèmes que j'ai rencontré avec BeanFiles pour la mise en place :
  • La documentation est très pauvre : une page sur le wiki, plus une classe de test.
  • BeanFiles est une librairie construite avec maven, mais je n'ai pas trouvé de repo maven associé, ce qui a compliqué la mise en place
  • La documentation est relativement pauvre, elle ne contient qu'un exemple de code, mais pas de tuto de mise en place, du coup j'ai été obligé d'aller voir dans le pom.xml du code source pour avoir les dépendances.


Étape 1 : Lecture du fichier CSV simple

La mise en place est relativement simple une fois que les problèmes ont été résolus :).
Mise à part les problèmes sités plus haut, BeanFiles n'aime pas du tout avoir des lignes vides à la fin du fichier.
Une autre limitation, les attributs de la classe doivent avoir les mêmes noms que les entêtes dans le fichier CSV.

La lecture est relativement simple :
public List<Dog> getDogs(InputStream stream) throws IOException {
    CSVReaderIterator<Dog> readerIterator = new CSVReaderIterator<Dog>(Dog.class, stream);
    stream.close();
    List<Dog> dogs = new ArrayList<Dog>();
    for (Dog dog : readerIterator) {
        dogs.add(dog);
    }
    return dogs;
}
J'aurais préféré que la fermeture du stream soit gérée par la librairie.
L'utilisation d'un iterator est pas bête, toutefois elle peux donner l'impression que la lecture du fichier se fait au fur et à mesure, alors que pas du tout :)
Du coup comme la lecture est finie après l'appel au constructeur, j'aurais bien aimé pouvoir récupérer directement la liste (un getAll() sur l'iterator).
Je trouve qu'avoir mis le parsing dans le constructeur n'est pas génial...

Étape 2 : écriture de fichier CSV

Il n'est pas possible d'écrire avec cette librairie.

Étape 3 : validation

Pas de validation non plus.

Étape 4 : parsing complexe.

Aucun problème avec les retour à la ligne pour BeanFiles.

Étape 5 : bench

Pour le bench, j'ai généré un fichier "moyen" (100 000 lignes) et un gros fichier (1 000 000 lignes).
Pour le fichier moyen, le temps moyen de traitement est de 1292ms.

Pour le gros fichier, il a fallu que je monte la JVM à 2Go. C'est un problème que je vois à BeanFiles, il n'y a pas de possibilité d'ajouter un handler pour effectuer un traitement au fur et à mesure de la lecture. Pour traiter de gros fichiers, c'est donc un gros problème.
Pour traiter ce fichier, le temps moyen de traitement est de 15120ms avec une consommation mémoire d'un peu moins de 1,6Go.

BeanIO

BeanIO est sûrement très bien, mais j'aime pas trop les configuration XML, donc je passe.

Commons-Csv

Je ne l'ai pas étudié non plus, pour deux raisons :
  • La version actuelle est la version 1.0-SNAPSHOT
  • C'est bas niveau, un peu comme open-csv


CsvToSql

Encore du XML... et en plus, ça n'a pas l'air adapté à ce que je veux faire.

FlatPack

Encore du XML...

JavaCSV

Les exemples de code montre que cette librairie ne fait pas vraiment du mapping : JavaCSV Samples

JCsv

Site : http://code.google.com/p/jcsv/

Étape 0 : Documentation et mise en place

La mise en place est très simple et documentée, juste la dépendance à ajouter et c'est parti.
C'est le gros point positif pour cette librairie la documentation est très riche.

Étape 1 : Lecture du fichier CSV simple

Étant fan du principe des annotations, je choisi d'utiliser cette méthode pour mon parsing :
public class Dog {
    @MapToColumn( column = 0)
    private String name;
    @MapToColumn( column = 1)
    private String race;
    @MapToColumn( column = 2)
    private String proprietary;
}
J'aurais préféré pouvoir utiliser la ligne d'entête mais bon...

La lecture n'est pas très compliquée non plus :
public List<Dog> getDogs(InputStream stream) throws IOException {
    Reader reader = new InputStreamReader(stream);

    ValueProcessorProvider provider = new ValueProcessorProvider();
    CSVEntryParser<Dog> entryParser = new AnnotationEntryParser<Dog>(Dog.class, provider);
    CSVReader<Dog> csvDogReader = new CSVReaderBuilder<Dog>(reader)
            .entryParser(entryParser)
            .strategy(new CSVStrategy(',', '"', '#', true, true)).build();

    return csvDogReader.readAll();
}
Lors de mon premier essai, je n'avais pas mis de "strategy", et l'exception remontée n'était pas très parlante (ArrayIndexOutBoundException)... Mis à part ça, je n'ai pas eu de d'autres problèmes.

Étape 2 : écriture de fichier CSV

L'écriture n'est pas très compliquée, par contre on ne peut pas utiliser les annotations, ce qui est un peu dommage.
public void writeFile(List<Dog> dogs, File file) throws IOException {

    CSVEntryConverter<Dog> entryConverter = new CSVEntryConverter<Dog>() {
        @Override
        public String[] convertEntry(Dog dog) {
            String[] columns = new String[3];
            columns[0] = dog.getName();
            columns[1] = dog.getRace();
            columns[2] = dog.getProprietary();

            return columns;
        }
    };
    CSVWriter<Dog> csvDogWriter = new CSVWriterBuilder<Dog>(new FileWriter(file))
            .entryConverter(entryConverter)
            .strategy(new CSVStrategy(',', '"', '#', true, true))
            .build();
    csvDogWriter.writeAll(dogs);
    csvDogWriter.close();
}
Le résultat n'est pas très bon, si les champs contiennent des retours à la ligne, il n'ajoute pas les caractères '"' avant et après. Il n'ajoute pas l'entête.
Bref, l'écriture existe, mais elle n'est pas satisfaisante de mon point de vue.

Étape 3 : validation

Pas de principe de validation.

Étape 4 : parsing complexe.

Aucun problème avec les retours à la ligne pour JCsv.

Étape 5 : bench

J'utilise les mêmes fichiers que pour BeanFiles.
Pour le fichier moyen, le temps moyen de traitement est de 39 219ms.

Pas de problème de consommation mémoire, JCsv permet de lire ligne par ligne, on a donc pas besoin de tout stocker dans une liste. Par contre les performances sont très mauvaises, cela provient du fait que JCsv appelle getAnnotations pour chaque ligne, et ne met rien en cache.

Le gros fichier confirme le bench avec le fichier moyen :
  • Temps de traitement moyen : 367 449ms


JSefa

Site : http://jsefa.sourceforge.net/

Étape 0 : Documentation et mise en place

La mise en place n'est pas documentée et il n'existe pas de repo maven (en tout cas je l'ai pas trouvé), par contre il suffit d'ajouter le jar.
Au niveau documentation, il existe une page avec les exemples basiques, pour des trucs plus complexes, il faut regarder la javadoc ou le code source.

Étape 1 : Lecture du fichier CSV simple

La déclaration du mapping via les annotations est plutôt simple :
@CsvDataType
public class Dog {
    @CsvField(pos = 0)
    private String name;
    @CsvField(pos = 1)
    private String race;
    @CsvField(pos = 2)
    private String proprietary;
}
J'aurais préféré pouvoir utiliser la ligne d'entête mais bon...

La lecture n'est pas très compliquée non plus :
public List<Dog> getDogs(InputStream stream) throws IOException {
    CsvConfiguration config = new CsvConfiguration();
    config.setFieldDelimiter(',');
    Deserializer deserializer = CsvIOFactory.createFactory(config, Dog.class).createDeserializer();

    List<Dog> dogs = new ArrayList<Dog>();

    deserializer.open(new InputStreamReader(stream));
    while (deserializer.hasNext()) {
        dogs.add(deserializer.<Dog>next());
    }
    deserializer.close(true);

    return dogs;
}
Pour filtrer l'entête on est obligé d'ajouter un Filter, un simple boolean dans les config aurait été appréciable...

Étape 2 : écriture de fichier CSV

L'écriture n'est pas très compliquée non plus :
public void writeFile(List<Dog> dogs, File file) throws IOException {
    CsvConfiguration config = new CsvConfiguration();
    config.setFieldDelimiter(',');
    Serializer serializer = CsvIOFactory.createFactory(config, Dog.class).createSerializer();

    serializer.open(new FileWriter(file));
    for (Dog dog : dogs) {
        serializer.write(dog);
    }
    serializer.close(true);
}
Toujours pas moyen d'ajouter l'entête.

Étape 3 : validation

Il existe une couche de validation, par contre elle n'est pas documentée, et je n'ai pas réussi à la faire fonctionner (n'hésitez pas à corriger mon code sur github si vous savez comment faire :) ).

Étape 4 : parsing complexe.

Aucun problème avec les retour à la ligne pour JSefa.

Étape 5 : bench

J'utilise les mêmes fichiers que pour BeanFiles.
Pour le fichier moyen, le temps moyen de traitement est de 791ms.

Le gros fichier confirme le bench avec le fichier moyen :
  • Temps de traitement moyen : 7 652ms


open-csv

C'est une librairie bas niveau (utilisée par la plupart des parseurs haut niveaux), je l'étudierai donc pas ici.

Ostermiller CSV

Dans cette librairie, le mapping se fait à la main, je ne l'étudierai donc pas.

Skife CSV

Encore une librairie bas niveau.

Super CSV

Site : http://supercsv.sourceforge.net/

Étape 0 : Documentation et mise en place

La mise en place n'est pas documentée, c'est pas du maven, dont pas si simple que ça, faut bien penser à mettre les deux jar dans les dépendances...
De manière générale la documentation est plutôt pas mal (malgré le fait qu'il n'existe pas de documentation pour la mise en place).

Étape 1 : Lecture du fichier CSV simple

Pas d'annotation pour cette librairie, tout se fait par le nom des attributs.
Pour les options il faut passer par des CellProcessors, j'y reviendrai pour la validation.

La lecture n'est pas très compliquée :
public List<Dog> getDogs(InputStream stream) throws IOException {
    List<Dog> dogs = new ArrayList<Dog>();

    ICsvBeanReader inFile = new CsvBeanReader(new InputStreamReader(stream), CsvPreference.STANDARD_PREFERENCE);
    final String[] header = inFile.getCSVHeader(true);
    Dog dog;
    while( (dog = inFile.read(Dog.class, header)) != null) {
        dogs.add(dog);
    }
    inFile.close();
    return dogs;
}
La gestion de l'entête et l'itération se fait à la main, je trouve ça dommage. Pour le reste c'est plutôt efficace.

Étape 2 : écriture de fichier CSV

Pour l'écriture, il n'y a pas de gestion de mapping, tout ce fait à la main, du coup je ne l'étudierai pas ici.

Étape 3 : validation

Pour la validation, c'est plutôt efficace, par contre cela repose sur l'ordre des champs, un peu dommage.
Il faut donc déclarer un tableau de CellProcessor :
public static final CellProcessor[] userProcessors = new CellProcessor[] {
        new NotNull(),
        new IsIncludedIn(new HashSet<Object>(DogValid.POSSIBLE_RACES)),
        null
};
On passe ensuite ce tableau pour le parsing des lignes :
inFile.read(DogValid.class, header, userProcessors)


Étape 4 : parsing complexe.

Aucun problème avec les retours à la ligne pour Super Csv.

Étape 5 : bench

J'utilise les mêmes fichiers que pour BeanFiles.
Pour le fichier moyen, le temps moyen de traitement est de 749ms.

Le gros fichier confirme le bench avec le fichier moyen :
  • Temps de traitement moyen : 7 523ms


CsvEngine

Pour ceux qui ne le savent pas, je suis le développeur de cette librairie, je ne suis donc sans doute pas très objectif :).
Site : https://github.com/ybonnel/CsvEngine

Étape 0 : Documentation et mise en place

La mise en place est très simple et documentée : Wiki install.
De manière générale, entre le wiki, la javadoc et les tests, je pense objectivement que CsvEngine est la librairie la plus documentée de celles que j'ai testées.

Étape 1 : Lecture du fichier CSV simple

Il faut tout d'abord ajouter les annotations à la classe dog :
@CsvDataType
@CsvFile
public class Dog {
    @CsvColumn("name")
    private String name;
    @CsvColumn("race")
    private String race;
    @CsvColumn("proprietary")
    private String proprietary;
}
Un truc qu'il faudra ajouter dans CsvEngine et le fait de rendre le nom du champs CSV facultatif (déduit du nom de l'attribut).

La lecture est très simple :
public List<Dog> getDogs(InputStream stream) throws IOException, CsvErrorsExceededException {
    CsvEngine engine = new CsvEngine(Dog.class);
    return engine.parseInputStream(stream, Dog.class).getObjects();
}


Étape 2 : écriture de fichier CSV

L'écriture n'est pas plus compliquée que la lecture :
public void writeFile(List<Dog> dogs, File file) throws IOException {
    CsvEngine engine = new CsvEngine(Dog.class);
    engine.writeFile(new FileWriter(file), dogs, Dog.class);
}


Étape 3 : validation

Pour la validation tout passe par les annotations :
@CsvFile
public class DogValid {
    @CsvColumn(value = "name", mandatory = true)
    private String name;
    @CsvValidation(ValidatorRace.class)
    @CsvColumn(value = "race", mandatory = true)
    private String race;
    @CsvColumn("proprietary")
    private String proprietary;
    
    public static class ValidatorRace extends ValidatorCsv {
        @Override
        public void validate(String field) throws ValidateException {
            if (!POSSIBLE_RACES.contains(field)) {
                throw new ValidateException("The race \"" + field + "\" isn't correct");
            }
        }
    }
}
On lit ensuite la fichier comme d'habitude :
public List<DogValid> readDogsValid(InputStream stream) throws CsvErrorsExceededException {
   CsvEngine engine = new CsvEngine(DogValid.class);
   return engine.parseInputStream(stream, DogValid.class).getObjects();
}


Étape 4 : parsing complexe.

Aucun problème avec les retour à la ligne pour CsvEngine.

Étape 5 : bench

J'utilise les mêmes fichiers que pour BeanFiles. Pour le fichier moyen, le temps moyen de traitement est de 693ms.

Le gros fichier confirme le bench avec le fichier moyen :
  • Temps de traitement moyen : 7 028ms


Conclusion

Première conclusion, écrire un article aussi long avec l'éditeur de Blogger est une corvée... faut que je trouve autre chose pour mon blog.

Voici un petit tableau récapitulatif des tests que j'ai pu mené sur ces librairies :
Documentation Mise en place Lecture Écriture Validation Temps de traitement
BeanFiles - - = X X 15 120
JCsv + + - - X 367 449
JSefa - = - = - 7 652
Super CSV + = + - + 7 523
CsvEngine + + + + + 7 028
Légende :
  • X : N'existe pas
  • - : Existe mais pas terrible
  • = : Existe et marche plutôt bien
  • + : Existe et est vraiment bien fait :)
De mon point de vue tout à fait objectif CsvEngine est la meilleure librairie sur tout les points, son seul défaut est sans doute mon Anglais très approximatif.

mardi 8 mai 2012

Retour de Devoxx France : troisième journée

Après mon retour sur la première journée (ici) et la deuxième journée (ici), voici celui de la troisième et dernière journée.

Les keynotes

Après avoir avalé un nombre de cafés important (la fatigue commençant à se faire sentir), je me dirige vers la salle des keynotes. La salle est toujours aussi impressionnante (950 places).

Trends in mobile application development

Je vais pas pouvoir vraiment vous parler de cette keynote, elle était réalisée par IBM, et tout le monde s'endormait...
La keynote était tellement intéressante que tout le monde se lâchait sur twitter. La seule chose marrante de cette conf a été la fin, où Nicolas Martignole nous a dit que le tweet wall était en panne et qu'il ne pouvait pas l'afficher, belle manière de ne pas mettre mal à l'aise un des sponsors de Devoxx :).

Portrait du développeur en "The Artist"

Rien à voir avec la conf précédente, c'est plutôt l'inverse...
Patrick Chanezon nous livre son portrait de la vie classique d'un développeur, passionnant. Je ne peux que vous conseiller d'aller voir les slides sur slide-share.
Le sujet principale était le cloud, mais cette conf allait beaucoup plus loin et j'ai adoré.

Abstraction Distractions for France

Neal Ford nous parle de couches abstractions, je pense que c'est une des conf les plus impressionnante que j'ai vu, Neal est un très bon orateur et la conf est très bien préparée. Ça ressemblait à une key-note Apple :).

Kotlin ou CodeStory?

Finnalement je suis retourné à CodeStory, si vous ne savez pas ce que c'est, aller voir mon billet sur la deuxième journée :).
Toujours aussi intéressant, cette fois j'ai ramener mon Ordi, j'ai donc même forké et mis en place l'environnement de dev, mais le temps d'installer ZombyJS, j'ai pas pu aller beaucoup plus loin (il fallait installer Node.js...).
Kotlin est lui un nouveau langage créé par Jetbrain, j'irai voir la conf quand elle sera disponible sur Parleys.

Changeons la conception de nos applications grâce aux services Cloud

Cette conf donnée par Cyrille Le Clerc est intéressante mais je ne l'ai pas trouvé adaptée au publique, pas assez de code et beaucoup trop de présentation de produits... Elle avait cependant l'intérêt de faire un inventaire des produits, je me suis noté qu'il fallait que la revoie lorsqu'elle sortirai sur Parleys.

An overview of Guava : Google Core Libraries for Java

Ben en fait non, quand je suis arrivé devant la porte, c'était déjà plein, et je me suis donc fait refoulé...
Du coup, je suis retourné voir CodeStory, chose marrante ils ont mis en place un système de Cache à l'aide de Guava :).

Java Caching with Guava

Après avoir vu une utilisation du cache avec Guava, je vais voir la conf théorique sur le sujet. En toute franchise, je me suis rendu compte que rien ne vaux la pratique. J'ai l'impression d'avoir plus appris avec les 10 minutes d'explications par la pratique qu'avec les 55 minutes de conf théorique.
Pour ceux qui ne connaissent pas Guava, je pense que c'est la première dépendance à ajouter sur un projet. Guava c'est apache-common en moderne et mieux fait :).

Android, Graphisme et Performance

Romain Guy (développeur de la couche graphique d'android) nous livre quelques astuces pour réaliser des applications Android qui marchent bien.
Un des trucs que j'ai noté (et que je n'ai pas encore eu le temps de tester), c'est le StrictMode qui permet de faire planter l'application dès qu'on fait un accès à une ressource lente dans le UI Thread. Cela permet de corriger tous les accès qui sont fait dans le UI Thread et qui ne devraient pas.

Les Cast Codeurs Podcast

Pour la dernière "conf" de la journée, je suis aller voir l'enregistrement live des Cast Codeurs. Pour ceux qui ne connaissent pas, c'est LE Podcast Java en France. L'ambiance était excellente, Atlassian distribuait des bières ce qui ne gâcher rien :). Si vous voulez vous rendre compte de l'ambiance, il suffit d'écouter l'épisode associé (ici).
Pour les bretons, il y aura un autre enregistrement live au Breizhcamp.


Toutes les conférences de Devoxx France étaient filmées et seront disponible sur Parleys au fil de l'eau durant l'année en gratuit ou rapidement en payant (les participants y ont accès tout de suite).
Je tiens à remercier les organisateurs (surnommés les polos rouges), et vivement l'année prochaine!

mardi 1 mai 2012

Retour de Devoxx France : deuxième journée

Après mon retour sur la première journée à Devoxx France (billet ici), voici celui de la deuxième.

Les keynotes

Après le deuxième petit dèj, voici le temps des keynotes. La salle était impressionnante (950 places).

Fier d'être développeur?

Pierre Pezzardi, co-fondateur d'OCTO, nous fait part des raisons pour lesquelles, selon lui, le métier de développeur est mal vu. Le développeur, c'est le gars qui crée des logiciels trop compliqués, qui ne plaisent pas aux utilisateurs, et sans jamais demander leurs avis. Il faut donc prendre le temps de comprendre un peu plus la finalité des fonctionnalités que l'on développe. Il est possible que prendre 5 minutes pour déplacer un bouton est bien plus d'impact pour les utilisateurs que de mettre des mois à développer des fonctionnalités obscures sans vraiment comprendre à quoi ça sert.

This could be Heaveb or this could be Hell

Bon, je l'avais évité jusque là, mais ça y est, voici le moment de ma première conf en anglais...
Deux développeurs du London JUG nous donnent la vision angélique et diabolique de l'avenir de Java. Ça manquait pas d'humour, et ils ont pensé à ceux, qui comme moi, ont du mal avec l'anglais en mettant des traductions de leur slide en français :). Je n'ai pas appris grand chose, mais on s'est bien marré pendant cette keynote.

Manipulation de bytecode : démocratisons la magie noire

Je me suis promis qu'un jour je jouerai avec le bytecode, je suis donc allé voir cette conf afin de me donner des pistes. Les speakers nous ont pointé un usage auquel je n'aurais pas pensé : les tests. Injecter du bytecode peux permettre de tester des cas compliqués type filesystem full. J'ai également retenu deux outils à aller voir de plus près : byteman et javassist.

Le déjeuner

À l'image de celui du premier jour, celui-ci était bien sans être exceptionnel. Il fut encore plus compliqué de trouver une place pour manger :)

Code Story

Ne trouvant pas de conf qui me donne envie, je décide d'aller faire un tour à Code Story. Il s'agit donc de quatre développeurs qui codent en pair programming avec des itérations d'une heure, et ce pendant 2 jours. Un des deux groupes prend le temps d'expliquer ce qu'il fait tandis que l'autre code plus vite. Au bout d'une demi heure, deux développeurs changent de paire.
C'est donc l'occasion de découvrir des outils et manières de développer pour être plus productif. Ils développent sous IntelliJ (pour ceux qui n'ont jamais essayé cet IDE, je vous conseille fortement de le faire). Les tests sont toujours développés en premier en utilisant Infinitest pour vérifier l'état en permanence. Autre idée, le build incassable, ils utilisent un script pour effectuer le push, ce script lance les TUs, et si certains ne passent pas, pas de push.
Comme sur tout les projets, ils ont rencontré des difficultés (problème de syntaxe en css par exemple), ce qui a permis de les rendre plus humain :)
La salle de Code Story était pleine à chaque fois, ce qui prouve que ce genre d'exercice a largement sa place dans une conf.

Kanban pour les nuls

Cette conf était très intéressante, Kanban est une méthode agile qui m'attire énormément. Si vous vous intéressez aux méthodes agiles, je ne peux que vous conseiller de regarder Kanban de près. Cette méthode est beaucoup moins contrainte que Scrum et se base énormément sur les fameux post-it sur un paper board. Mais ce n'est pas aussi simple que ça, l'ajout de workload sur les états permet de contraindre le flux et d'introduire le "flux tiré". Le principe est qu'une fonctionnalité ne rentre dans le flux qu'une fois qu'une fonctionnalité en est sortie. Cela est bien entendu un peu plus complexe que ça, mais j'aurais du mal à tout vous expliquer :).
Une seule question me taraude l'esprit, c'est comment appliquer ce genre de méthode lorsque le périmètre et les jalons sont imposés!

Code Story again

Comme vous vous en doutez, le principe m'a plu, du coup j'y retourne :)

Introduction à Ceylon

Ceylon, pour ceux qui n'en ont pas entendu parler, c'est un nouveau langage. Le but de ce langage : faire le langage idéal pour les javaistes si on pouvait partir de la feuille blanche (pas de rétro-compatibilité à gérer). Emmanuel Bernard et Stephane Epardaud nous présentent donc ce nouveaux langage, avec du code qui compile! Au niveau road map, l'interopabilité avec Java vient d'être introduite, on va donc pouvoir commencer à faire des choses intéressantes.
Au delà du sujet, le fait que ce soit présenté par Emmanuel m'a bien plu. Pour ceux qui ne le connaissent pas, c'est un des castcodeurs (podcast à écouter d'urgence). Donc le voir en vrai était plutôt sympa, c'est en plus un très bon speaker.


À bientôt pour la troisième journée :).

mardi 24 avril 2012

Retour de Devoxx France : première journée

J'ai eu l'occasion de participer à LA grosse conf Java en France, et franchement c'était que du bon.

Les rencontres

Un des trucs les plus plaisant à Devoxx a été les rencontres que j'ai pu faire et les discussions que j'ai pu avoir avec les speakers et participants. Un exemple : c'est pas tous les jours qu'on a l'occasion de discuter avec un développeur de Montain View qui bosse sur Android (Romain Guy en l'occurrence). Ce n'est pas non plus tous les jours qu'on a l'occasion de développer avec un développeur d'exception (David Gageot).

Je vais donc maintenant essayer de vous faire un petit compte rendu de ces trois jours qui furent très riches!

Premier jour

Première étape : le café, et les discussions avec les têtes connues et moins connues.
Pour ma part je suis arrivé à 8h tous les matins, ce qui m'a permis de prendre le temps de discuter, les confs ne commençant qu'à 9h30.

La première journée est un peu particulière, elle est principalement consacrée à des "Universités" et des "Hands-on Labs" qui sont des sessions de 3 heures avec pour les Labs de la manip sur son ordi. Ça laisse donc le temps de rentrer un peu plus dans le détail du sujet, et un peu moins le survoler.

Les 3 A pour Java EE 6

Cette session était donc consacrée à Java EE6. Elle s'est déroulée sous forme de TP permettant de mettre en pratique les nouveautés JEE6. Le TP s'appuyait sur le projet exemple initié par Antonio Goncalves.
J'ai profité de cette session pour tester NetBeans, et franchement, pour faire du JEE, c'est juste excellent. On était 2 sous NetBeans sur 40 personnes environ. Ceux qui utilisaient Eclipse ont commencé à avoir un environnement de dev fonctionnel au bout d'un peu plus d'1h, alors que j'avais quasiment fini. J'avais tout de même anticipé en préparant l'environnement la veille. L'absence d'internet sur place rendant toute installation compliquée.
J'ai donc pu mettre en pratique CDI et JAX RS, et j'ai plutôt été agréablement surpris. On va peut être pouvoir enfin se passer de Spring :). Les speakers étaient très disponibles et plutôt sympas (ce qui était le cas pour tout les speakers, je ne vais donc pas le répéter :) ).

Après cette session, une petite pause déjeuner. J'ai pu manger à ma faim, et sans être exceptionnel (ça vaut pas les galettes saucisses du breizhcamp), c'était plutôt bon, merci donc aux organisateurs. Encore une occasion de discuter et partager avec plein de monde.

Hackergarten Paris

Ne trouvant pas de sujet qui m'intéresse particulièrement, je décide d'aller au Hackergarten. J'ai donc pu découvrir ce que c'est que cette chose imprononçable. C'est en fait l'occasion de contribuer à un projet open-source. La session commence par un inventaire des projets open-source représentés (par un contributeur). Voici ce qui est ressorti de l'inventaire (du moins ceux dont je me souviens) :
  • Maven
  • Groovy
  • Jenkins
  • Infinitest
  • Je sais plus mais y en avait d'autre :)
Il y avait donc du lourd, j'ai décidé de contribuer à Infinitest. Ce projet se concrétise par un plugin Eclipse et un plugin Intellij permettant d'avoir un état des tests unitaires en permanence dans l'IDE. Dans Eclipse, à chaque build (autant dire tout le temps), le plugin analyse les deltas afin de lancer les tests potentiellement impactés par la modification.
Nous nous somme retrouvés à deux pour contribuer à Infinitest encadrés par David Gageot qui est le Leader du projet. Pendant que David nous présente le code, j'installe l'environnement de dev. David nous présente également une idée de fonctionnalité à réaliser. Actuellement le plugin analyse les dépendances entre classes afin de sélectionner les tests à exécuter, mais si un test est impacté par une modification de fichier plat, il n'est pas exécuté. Sans rentrer dans les détails, cette session c'est traduite par une pull request dans github, c'était donc plutôt concret. Cela m'a permis de découvrir le développement de plugin Eclipse, et toute la difficulté pour réaliser des tests automatisés dans ce genre d'environnement.



Les tools in action

La journée s'est terminée pour ma part par trois tools in action, ce format est un format intense : 30 minutes pour présenter un sujet.

Le premier était sur Selenium Grid, qui permet de mettre en place un pool de browser qui serviront ensuite aux tests automatisés avec Selenium. Malgré toutes les difficultés qu'a pu rencontrer Mathilde Lemee pour les démos, cela donne une bonne idée du fonctionnement de l'outil et donne envie de tester.

Le suivant était sur AndroidAnnotations : sur scène deux speakers (Pierre-Yves Ricau et Alexandre Thomas). AndroidAnnotations est un framework dont le but est de simplifier les développements Android en utilisant massivement les annotations. Les 30 minutes ont quasiment été entièrement utilisées à du live-coding, ça aurait pu être un exercice casse gueule, mais les deux speakers ont préparé leur truc et tout se déroule sans encombre. Résultat : faut que j'essaye, et j'ai bien envie de contribuer au projet.

La tout dernière session fue consacrée aux lambdas en actions avec la beta du JDK8. Ce que j'ai retenu : enfin une évolution majeure du langage Java. Une conséquence marrante des Lambdas est la quasi introduction du polymorphisme (les interface peuvent maintenant avoir des implémentations par défaut pour les méthodes).

L'article sur la deuxième journée arrivera un peu plus tard :)

Les photos ont été récupérées sur les albums de Claude

mercredi 22 février 2012

Bench de MoteurCsv

Suite à la publication du billet MoteurCsv le JPA du CSV, quelqu'un m'a demandé si j'avais fait un bench. N'en ayant pas fait, je me suis lancé.
Les sources du bench sont disponibles sur github

Génération du fichier de test

Le "cahier des charges" fournit par "bbo" était le suivant :
  • ~10 000 000 de lignes.
  • ~200 caractères par ligne.
  • ~20 champs (4/5 gros champs autour de 20 caractères.)
C'est avec ces contraintes que j'ai créé la classe GenerationFchierCsv.
Cette classe génère donc un fichier de 10 000 000 de lignes contenant des lignes avec 20 champs :
  • Quatre champs de type String et de 30 caractères
  • Quatre champs de type Boolean (1 ou 0)
  • Quatre champs de type Integer et de 5 chiffres
  • Quatre champs de type Double et de 5 chiffres avant la virgule et 5 chiffres après la virgule
  • Quatre champs de type String et de 5 caractères

Voici le résultat de la génération du fichier :
Statistiques sur la taille des lignes :
  • Minimum : 215 caractères
  • Maximum : 227 caractères
  • Moyenne : 225,666 caractères
Taille du fichier : 2 266 666 035 octets soit 2,11 Go
Génération en 70 576ms
Le résultat est donc à peu près conforme au cahier des charges.

Constitution du bench

Le bench est réalisé par la classe Bench (je sais, je suis trop bon pour trouver des noms).
Pour le bench, je ne pouvais pas utiliser la méthode permettant de parser tout le fichier et qui renvoie une liste d'objet. Vu la taille du fichier, il est facile de comprendre que le mettre entièrement en mémoire ne serait pas une bonne idée.
Heureusement le moteur contient une autre méthode permettant de réaliser un traitement pour chaque ligne : MoteurCsv.parseFileAndInsert
Dans le cadre du bench j'ai donc utilisé cette méthode en ne réalisant aucun traitement :
public static void bench1() throws FileNotFoundException {
    long startTime = System.currentTimeMillis();
    moteur.parseFileAndInsert(new FileReader(fichier), ObjetCsv.class,
        new InsertObject<objetcsv>() {
            @Override
            public void insertObject(ObjetCsv objet) {
                // On ne fait rien dans le cadre du bench.
            }
        });
    long elapsedTime = (System.currentTimeMillis() - startTime);
    System.out.println("Lecture du fichier : " + elapsedTime + "ms");
}

J'ai également créé une méthode permettant de voir l'utilisation mémoire au fur et à mesure du test.
Le but de cette méthode est de regarder la mémoire occupée avant et après un GC entre chaque itération du bench. Cela permettra de vérifier entre autre qu'il n'y ait pas fuite mémoire.
public static void gestionMemoire() {
    // Mémoire totale allouée
    long totalMemory = Runtime.getRuntime().totalMemory();
    // Mémoire utilisée
    long currentMemory = totalMemory - Runtime.getRuntime().freeMemory();
    System.out.println("Mémoire avant gc : " + (currentMemory / 1024) + "ko/" + (totalMemory / 1024) + "ko");
    System.gc();
    // Mémoire totale allouée
    totalMemory = Runtime.getRuntime().totalMemory();
    // Mémoire utilisée
    currentMemory = totalMemory - Runtime.getRuntime().freeMemory();
    System.out.println("Mémoire après gc : " + (currentMemory / 1024) + "ko/" + (totalMemory / 1024) + "ko");
}

Résultats

Les tests ont été menés sur un MacBookPro équipé d'un disque SSD et d'un Code i5.
Étape Temps Mémoire occupée avant GC Mémoire occupée après GC
Avant de commencer / 1 734ko 338ko
Instanciation du moteur 58 222µs 14 049ko 387ko
Lecture du fichier (itération 1) 65 902ms 14 049ko 387ko
Lecture du fichier (itération 2) 65 864ms 13 796ko 341ko
Lecture du fichier (itération 3) 64 638ms 14 059ko 341ko
Lecture du fichier (itération 4) 65 214ms 13 700ko 341ko
Lecture du fichier (itération 5) 66 560ms 14 027ko 341ko

Ces résultats permettent de montrer plusieurs choses :
  • Temps pour instancier le moteur : quasi-null
  • Mémoire persistante pour le moteur : quelques Ko
  • Performances plutôt satisfaisantes avec un peu plus d'une minute pour lire un fichier de plus de 2Go

J'ai également fait du profiling avec YourKit afin de vérifier le comportement interne du moteur, cela a montré que la majorité du temps est passé dans la librairie open-csv, l'overhead du moteur est donc plutôt faible.

Reproduire le bench

N'hésitez pas à reproduire le bench et à me dire les résultats que vous obtenez :
  • Cloner le projet depuis github : github.com/ybonnel/BenchMoteurCsv
  • Importer le projet en tant que projet maven dans Eclipse
  • Lancer d'abord le main de la classe GenerationFchierCsv afin de générer le fichier de test
  • Lancer ensuite le main de classe Bench afin de lancer le bench en lui-même
Si vous connaissez d'autres parseurs CSV n'hésitez pas à ajouter des benchs dans le projet pour comparer les performances avec d'autres parseurs.

lundi 20 février 2012

MoteurCsv le JPA du CSV

Voici un titre qui va faire gonfler mes chevilles :)
Je vais donc vous présenter une petite librairie Java sans prétention que j'ai réalisé afin de pourvoir lire et écrire des fichiers CSV facilement.

C'est quoi le CSV?

Bon pour la définition du format CSV, je vous invite à consulter l'article wikipedia.

Pourquoi cette librairie?

Cette librairie a été crée afin de pouvoir gérer le format GTFS. Ce format est utilisé afin de décrire un réseau de Transports en commun. Techniquement, c'est un zip contenant des fichiers CSV.
J'ai eu à utiliser ce format suite à l'ouverture des données de transports à Rennes où j'ai réalisé une application Android (Transports Rennes) permettant de consulter les horaires de bus.
A l'époque où j'ai créé cette librairie, je baignais dans du JPA la journée, et j'avoue que j'aime beaucoup l'idée de décrire du mapping directement dans la classe associé en utilisant des annotations.
C'est comme ça que MoteurCsv est né.

Alors comment ça s'utilise?

J'ai essayé de simplifier au maximun l'utilisation de la librairie.

Installation

Maven

Si vous êtes sous maven, l'intégration dans votre projet est très simple.
Il suffit d'ajouter dans votre pom.xml :
<dependencies>
    <dependency>
        <groupId>fr.ybo</groupId>
        <artifactId>moteurcsv</artifactId>
    </dependency>
</dependencies>

<repositories>
    <repository>
        <id>ybonnel-release</id>
        <url>https://repository-ybonnel.forge.cloudbees.com/release/</url>
    </repository>
</repositories>

Autre

Si vous n'êtes pas sous maven, il vous suffit d'intégrer les deux jars suivant :

Utilisation

Maintenant le vif du sujet, comment utiliser cette petite librairie.
Afin de pouvoir lire ou écrire un CSV il faut commencer par décrire la classe correspondante.
On va prendre pour l'exemple un CSV décrivant des personnes avec deux colonnes : "nom" et "prenom".
Voici un exemple correspondant :
nom,prenom
Bonnel,Yan
Vador,Dark
Noël,Père

Déclaration de la classe

Nous allons donc créer la classe Personne associée avec les annotations permettant de faire le mapping :

// Annotation permettant de dire au moteur que cette classe est associée à un fichier CSV.
@FichierCsv
public class Personne {

    // Annotation permettant de dire au moteur que cet attribut est mappé avec la colonne "nom" du CSV.
    @BaliseCsv("nom")
    private String nom;

    // Annotation permettant de dire au moteur que cet attribut est mappé avec la colonne "prenom" du CSV.
    @BaliseCsv("prenom")
    private String prenom;
}

Création du moteur

MoteurCsv moteur = new MoteurCsv(Personne.class);

Lecture d'un fichier CSV

Voici maintenant le code permettant de lire le fichier, et de le transformer en une liste d'objets :
InputStream stream = new FileInputStream(new File("personnes.csv"));
List<Personne> personnes = moteur.parseInputStream(stream, Personne.class);

Écriture d'un fichier CSV

Et le core permettant d'écrire un fichier :
Writer writer = new FileWriter(new File("personnes.csv"));
moteur.writeFile(writer, personnes, Personne.class);

Conclusion

J'espère que cette petite librairie sera utile à quelqu'un d'autre que moi :)
Elle est open-source (LGPL v3) : github.com/ybonnel/MoteurCsv
N'hésitez donc pas à forker et à faire des pull request!
Site généré contenant entre autre la javadoc : ybonnel.github.com/MoteurCsv

mercredi 8 février 2012

Transports Rennes - Résultat des votes

Après huit jours de votes (ici), voici les résultats pour le vote de la nouvelle icône de l'application Transports Rennes (Market).

J'ai tout d'abord été agréablement surpris par la participation : 96 votes!
En toute franchise, je m'attendais plutôt à une trentaine de votes max, donc merci à tous pour votre participation.

Voici les résultats :
Logo 01 - Cercle37 votes
Logo 07 - Etoiles17 votes
Logo 06 - Avec Ligne13 votes
Logo 00 - Actuel12 votes
Logo 02 - Cercle avec ombre7 votes
Logo 05 - Avec Panneau gris6 votes
Logo 03 - Station floutée3 votes
Logo 04 - Avec Panneau bleu1 votes

La prochaine icône de Transports Rennes sera donc :


Merci à l'auteur pour sa proposition (et ces 5 autres...) : Greg - @gregbzh35

lundi 23 janvier 2012

Transports Rennes - Votez pour vos icônes préférés.

Suite à plusieurs propositions de nouveaux visuels pour l'application Transports Rennes (Market), je lance un vote afin que les utilisateurs puissent choisir les icônes qu'ils préfèrent.

Voici les propositions que j'ai reçu (la petite image correspond à la résolution réelle sur une téléphone actuel) :

Logo 00 - actuel (proposé par Nicolas www.cryptik-web.com) :

Logo 01 - Cercle (proposé par Greg - @gregbzh35) :

Logo 02 - Cercle avec ombre (proposé par Greg - @gregbzh35) :

Logo 03 - Station floutée (proposé par Greg - @gregbzh35) : 

Logo 04 - Avec Panneau bleu (proposé par Greg - @gregbzh35) :

Logo 05 - Avec Panneau gris (proposé par Greg - @gregbzh35) :

Logo 06 - Avec Ligne (proposé par Greg - @gregbzh35) :

Logo 07 - Etoiles (proposé par @yesnault) :


Place aux votes (désolé pour le thème, j'ai rien trouvé de mieux...).
Merci de ne voter qu'une seule fois.

Les votes sont clos!