vendredi 23 décembre 2011

CodeRetreat@Lille#2 samedi 14 janvier 2012

Le samedi 14 janvier 2012, la société Proxiad organise au sein de ses locaux un code retreat. Pour s'inscrire, c'est par ici. Attention toutefois car les places sont limitées.

Mon challenge : écrire au moins un de mes programmes en ceylon et construire mon projet à l'aide de graddle ... suspens!

samedi 17 décembre 2011

Atelier GIT chez Ineat Conseil

Jeudi 15 décembre 2011, la société Ineat Conseil a organisé dans ces locaux une présentation suivie d'un atelier sur le système de gestion de source GIT par Sébastien Douche (@sdouche), animateur du blog #gitfr entièrement dédié à GIT.

mercredi 14 décembre 2011

Android SOAP Enabler 1.0

La société de service informatique Norsys contribue une fois de plus au monde du logiciel libre. Après JDBCAdmin et Speculoos, c'est au tour de Android SOAP Enabler de faire son comming out free.
Derrière ce nom ce cache un kit de développement sous licence GNU Lesser GPL permettant aux développeurs d'applications pour le système d'exploitation mobile Android de connecter leurs applications à des WebServices SOAP.

Pour en savoir plus, suivez le guide.

The french I.T. service company Norsys contributes again to the world of Free Software. After JDBCAdmin and Speculoos, it's now Android SOAP Enabler that comes free opensource software. This project is a toolkit under GNU Lesser GPL allows Android application's developer to connect their software to SOAP WebServices.

Ask for more ? Follow this link.

lundi 5 décembre 2011

Changer une variable d'environnement utilisateur sous Win 7

Voici la marche à suivre afin de modifier une variable d'environnement sous Windows 7, surtout quand l'utilisateur ne dispose d'aucun droit d'administration.

  • Cliquer sur Démarrer
  • Dans le champs de recherche, entrer la chaîne «variables environnement»
  • Choisir «Modifier les variables d'environnements pour votre compte»
Yapluka!

Source [en]

lundi 28 novembre 2011

Test Code Coverage in Eclipse

Voici un plugin Eclipse rapide est efficace pour connaître son taux de couverture de code par les tests. J'utilise la version 2.0.0 beta et le résultat est très classe.

Bon maintenant faut que j'écrive plus de tests ... ;)

mardi 22 novembre 2011

Session Spring Batch avec Ekino le 29 Novembre 2011

Le Ch'ti JUG, le Java User Group des gens du nord, organise le 29 Novembre 2011 à l'IUT A de Lille 1 une session sur Spring Batch avec la participation de Ekino

En savoir plus

dimanche 6 novembre 2011

CodeRetreat@Lille#1

En ce samedi 05 novembre de l'an 2011 s'est tenu à Lille, Nord pas de Calais FRANCE, le premier code retreat de la région dans les locaux de la société EFIDEV.

C'est quoi ça un code retreat ?

Une retraite de développeurs consiste en une journée de partage sur le développement d'une solution à un problème donné.
c'est qui ?
C'est un ensemble de développeurs portant souhaitant s'améliorer dans leur activité principale, ainsi qu'un "coach" organisant la retraite
c'est où ?
C'est dans un endroit permettant à un groupe de s'organiser en paire, chacune utilisant un ordinateur pour développer sa solution
c'est quand ?
C'est sur une journée décomposer en round de 45 minutes permettant aux paires de développer une solution, suivi d'un break permettant au groupe d'échanger sur le déroulement du round précédent

Déroulement de la journée du 05 novembre

09:00acceuil + socialisation
09:45présentation code retreat et sujet
10:00iteration 1 sans contraintes particulières
10:45break, suppression du code
11:25iteration 2 contrainte de simple design
12:10break, suppression du code
12:35pause déjeuner
13:45iteration 3 contrainte TDD as if you meant it
14:30break, suppression du code
14:40iteration 4 contraintes sans if, sans while, sans structures de données, méthode de moins de 4 lignes
15:30break, suppression du code
15:50iteration 5 contraintes
16:35break, suppression du code, bilan de la journée et départ pour moi
Chaque itération consistait à résoudre le problème du jeu de la vie dans n'importe quel langage de programmation mais en respectant des contraintes proposées en début d'itération. A l'issue d'une période de 45 minutes, une sonnerie marque la fin de l'itération (delete your code now!, delete your code now!) ainsi que l'ordre de supprimer tout code produit laissant une place net pour la prochaine itération. Un échange d'un quart d'heure permettait au groupe d'échanger sur le déroulement de l'itération précédente.

Bilan de cette journée

La première récompense de cette journée est un magnifique mal de crâne du à une journée entièrement dédiée au pair programming et à la programmation dans des conditions toujours plus contraignantes à chaque itération. Petit bémol, j'ai toujours effectué l'exercice dans mon langage de programmation de prédilection, mais d'autres membres du groupe ont eu l'occasion d'entrer en contact avec d'autre langage tels JavaScript, C#, et Ruby. Au cours de cette journée, une seule itération m'a permis de résoudre l'ensemble des règles du jeux de la vie et c'est la quatrième, sans le droit d'utiliser de if, ni de boucle. Et notre solution à abouti à des méthodes de moins de 2 lignes. Un résultat inattendu montrant que les contraintes ne sont pas forcément des freins.

J'ai aimé

J'ai apprécié cette image : les musiciens passent beaucoup de temps en répétition et peu de temps sur scène alors que le développeur passe l'essentiel de son temps sur scène et rarement en répétition. Le code retreat est une forme de répétition venant s'ajouter au coding dojo et tout autre forme d'échange entre développeurs.

Remerciements

  • Je remercie tout d'abord le groupe pour la bonne ambiance et la bonne humeur qui ont accompagnés cette journée.
  • Je remercie également Jérémie Hattat pour m'avoir donné l'occasion de participer à cette journée.
  • "At last but not least", je remercie Aurélien Fontaine et la société EFIDEV d'avoir offert les moyens logistiques à l'organisation de cette évènement ainsi que Adrian Bolboaca le maître de cérémonie de cette journée.

mercredi 5 octobre 2011

L'agile tour à Lille en 2011

L'agile Tour, un des grands rendez-vous autour des démarches de développement agile, se produit sur Lille le 10 novembre 2011.

Pour plus d'information, consultez le programme

Pour s'inscrire c'est par ici

Utiliser Git derrière un proxy

Petite astuce pour utiliser Git à travers un proxy: positionner une variable d'environnement http_proxy (ou https_proxy dans mon cas) de la manière suivante:

$> export http_proxy="http://proxy.host.dom:1234"

et pour l'HTTPS:

$> export https_proxy="https://proxy.host.dom:1234"

Le clonage devient tout de suite plus simple

$> git clone http[s]://[username@]forge.host.dom/mon/repo.git

Voilà!

jeudi 23 juin 2011

Une tite taglib m'ssieurs dames !

Voici une TAGLIB sympatique pour générer simplement les tableaux :
displaytag

Fini les <table>, les boucles for, ... et en plus les colonnes sont triables, dingue!

dimanche 12 juin 2011

NAS + DLNA

Si je devais disposer d'un boitier NAS, j'opterai pour ce tuto illustrant la démarche de création d'un NAS fait mains.
J'ajouterai ce serveur DLNA permettant de mettre les flux multi media à diposition des périphériques compatibles comme les nouveaux téléviseurs ainsi que les consoles de jeux.

vendredi 13 mai 2011

Installer Oracle Weblogic Server 11gR1 sur Ubuntu 10.04 x86

Voici quelques détails sur l'installation du serveur d'application Oracle Weblogic 11gR1 (10.3.5). J'ai choisit d'installer ce serveur d'application ainsi que la machine virtuelle Java JRockit supportée par ce serveur.

Installation



  1. Télécharger la distribution de la JRockit (jrockit-jdk1.6.0_24-R28.1.3-4.0.1-linux-ia32.bin) à cette adresse,

  2. Ajouter les droits d'exécution au fichier (chmod) avant de l'exécuter et suivre l'assistant d'installation,

  3. J'ai choisit de déplacer le dossier contenant la JRockit dans le dossier /usr/lib/jvm/, à côté des autres machines virtuelles Java,

  4. Télécharger le ficher wls1035_dev.zip (~300 Mo) à cette adresse

  5. Dé-zipper le contenu de l'archive dans le répertoire de votre choix (j'ai choisit /opt/oracle/wls-10.3.5) que nous noterons <MW_HOME>

  6. Modifier le script configure.sh situé dans le dossier MW_HOME pour le faire utiliser /bin/bash en lieu et place de /bin/sh

  7. Lire attentivement et suivre les instructions situées dans le script README.txt fourni avec le serveur,

  8. J'ai choisit de créer le domaine /var/local/wlsdomains/localdomain en utilisant l'interface graphique via le script $MW_HOME/wlserver/common/bin/config.sh


Un peu de monitoring


Voilà, Weblogic 11gR1 est maintenant prêt à servir, JRockit est fournit avec JRockit Mission Control, une console d'administration de la machine virtuelle fortement utile pour inspecter en vol le fonctionnement des applications (Mémoire, CPU, Thread).
On pourra faire tourner le serveur avec un JVM classique et utiliser Visual VM présent depuis la version 1.6.0_07 dans $JAVA_HOME/bin/jvisualvm, ou alors utiliser JConsole présent depuis la JDK 1.5 dans $JAVA_HOME/bin/jconsole
Enjoy!

mardi 10 mai 2011

REST avec Jersey!

Les architectures REST sont une nouvelle forme d'architecture exprimant un certain retour aux sources. En effet, ce type d'architecture tire sa puissance des standards éprouvés du Web, en particulier du protocole HTTP.

Rappel HTTP


HTTP est un protocole orienté ressource, c'est-à-dire qu'il est dédié à la manipulation de ressources à travers un réseau et constitue un fondement de l'Internet et du World Wide Web. Il repose sur TCP et se traduit sous 2 formes :

La requête

Connexion effectuée depuis un client vers un serveur, elle identifie une ressource via son URL et donne un ordre à effectuer au serveur. Exemple :

GET http://www.google.com/

Les différents ordres sont :

  • (C) POST : pour créer une ressource,

  • (R) GET : pour lire une ressource,

  • (U) PUT : pour modifier une ressource,

  • (D) DELETE : pour supprimer une ressource,

  • ..., et bien d'autres encore, ...


Le format que nous utilisons tous (y compris en consultant ce billet) avec notre navigateur est codifié de la manière suivante : text/html. D'autre format sont également codifiés par la RFC 2046, on y retrouve notamment application/xml pour le format XML, application/json pour un flux JSON, ... Exemple :

Accept: application/xml, application/json, text/plain


La réponse

Connexion effectuée par le serveur vers le client ayant effectuée une requête afin de retourner le resultat de celle-ci. Elle est caractérisée par son code de retour, et eventuellement un contenu dont le format est précisé. Exemple :

HTTP/1.1 200 OK
<html>
...
</html>

Les codes de retour varient selon le résultat de l'exécution de la requête :

  • 200 : retour normal car tout va bien,

  • 201 : retour indiquant que la ressource a bien été créée,

  • 404 : la ressource n'a pu être trouvée

  • ..., et bien plus encore, ...



De la même manière qu'un client indique les représentations qu'il est en mesure de comprendre, le serveur indique dans l'entête de la réponse la représentation qu'il a choisit. Exemple :

Content-Type: text/html


Un premier pas en JEE


Maintenant que nous avons révisé les bases, allons plus loin avec un exemple d'implémentation en Java. Commençons par jetter un oeil sur la classe HttpServlet: avez vous remarqué ces méthodes, elles ne vous rappellent rien ?

Il s'agit des méthodes qu'il convient d'implémenter pour écrire une servlet orienté REST. Les codes de retour sont quant à eux disponibles sous forme de constantes dans la classe HttpServletResponse, c'est bien fait non ?

On passe la 6ième avec Jersey


L'écriture d'un servlet faisant du REST est une belle aventure à vivre mais on se heurte à une difficulté majeure : celle de savoir facilement interpréter une URL. C'est-à-dire comment savoir que

http://people.host.ext/user/guillaume.wallet/messages/20110101

est une URL listant les messages datés du premier janvier de l'année deux mille onze de l'utilisateur enregistré sous le nom 'guillaume.wallet', galère non?
JAX-RS est une spéficiation Java EE, incluse depuis la verion 6, ayant pour objet la standardisation de cette démarche REST au sein d'une application Java. L'implémentation de référence est fournit par le projet Jersey. Un tutorial complet est disponible sur le site.

Pom Pom Pom pommmm


Voici le pom qui m'a servi pour cette démarche:

<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.sample.jersy</groupId>
<artifactId>jersey-sample</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<inceptionYear>2011</inceptionYear>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.8</version>
<configuration>
<classpathContainers>
<classpathContainer>org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6</classpathContainer>
</classpathContainers>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
<wtpversion>2.0</wtpversion>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>${tomcat.version}</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
</dependencies>
<properties>
<tomcat.version>7.0.12</tomcat.version>
</properties>
<repositories>
<repository>
<id>maven2-repository.java.net</id>
<name>Java.net Repository for Maven</name>
<url>http://download.java.net/maven/2/</url>
<layout>default</layout>
</repository>
</repositories>
</project>

Bonjour toi!


Notre première resource:

package org.sample.jersey.rest;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

/**
* Une ressource polie.
*/
@Path( "/hello/{world}" )
public class HelloWorldResource
{
@GET
@Produces("text/plain")
public String greet(@PathParam("world") String world)
{
return "Hello " + world;
}
}

Et un peu de magie : pour tester notre application, il faut ... une application. Nous allons créer la classe suivante qui suffira à lancer notre premier test:

package org.sample.jersey.rest;

import javax.ws.rs.ApplicationPath;

import com.sun.jersey.api.core.PackagesResourceConfig;

/**
* La racine de l'application REST.
*/
@ApplicationPath( "/resources" )
public class SampleRESTApplication
extends PackagesResourceConfig
{
/**
* On explique à JERSEY que les Resources sont dans le paquet org.sample.jersey.rest.
*/
public SampleRESTApplication()
{
super( "org.sample.jersey.rest" );
}
}

Pour tester, une fois déployée dans le serveur de votre choix (tant qu'il s'agit de Tomcat dans sa version 7), rendez à l'URL suivante :

http://localhost:8080/<nom>/resources/hello/World

où <nom> remplace le nom de votre WebApp. Maintenant, remplacer "World" par ce qui vous chante et constater la magie: si peu de ligne pour un si bel effet.

«Comment tu le veux ta ressource ?»


L'exemple ci-dessous illustre la négociation de contenu.

package org.sample.jersey.rest;

import java.util.HashMap;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

import org.json.simple.JSONObject;

/**
* Gestion des gens.
*/
@Path( "/users/{username}" )
public class PeopleResource
{
@GET
@Produces( "application/json" )
public String showUserAsJSON( @PathParam( "username" ) String username )
{
User user = findUserByUsername( username );
return user.toJSON();
}

@GET
@Produces( "text/plain" )
public String showUserAsTextPlain( @PathParam("username") String username )
{
User user = findUserByUsername( username );
return user.toString();
}

public PeopleResource()
{
users.put( "guillaume.wallet", new User( "guillaume.wallet", "Guillaume", "Wallet" ) );
users.put( "linus.torvalds", new User( "linus.torvalds", "Linus", "Torvalds" ) );
}

private User findUserByUsername( String username )
{
User user = users.get( username );
return user;
}

/* Une vraie BDD. */
private HashMap users = new HashMap();

/**
* Euh, ... un utilisateur quoi!
*/
private static class User
{
private String username;

private String firstname;

private String lastname;

public User( String username, String firstname, String lastname )
{
super();
this.username = username;
this.firstname = firstname;
this.lastname = lastname;
}

@SuppressWarnings( "unchecked" )
public String toJSON()
{
JSONObject me = new JSONObject();
me.put( "username", username );
me.put( "firstname", firstname );
me.put( "lastname", lastname );
return me.toJSONString();
}

@Override
public String toString()
{
return String.format( "%1s %2s", firstname, lastname );
}
}
}

Selon que vous fassiez :

$> wget --header "Accept: application/json" http://localhost:8080/<nom>/resources/users/guillaume.wallet

ou

$> wget --header "Accept: text/plain" http://localhost:8080/<nom>/resources/users/guillaume.wallet

Le rendu de la ressource change.

Conclusion


Cet article ne constitue qu'un petit pas, c'est maintenant à vous de faire le REST.

mercredi 27 avril 2011

Un tuto pour apprendre Ruby

Ruby est un langage interprété orienté objet et un environnement d'exécution permettant l'exécution des programmes écrit dans ce langage.

Ce tutorial permet d'en savoir plus sur le langage et la façon de programmer.

samedi 23 avril 2011

Installer Ruby 1.9.2 et Rails 3 sur Ubuntu 10.04

Dans le cadre d'une démarche de découverte de l'environnement Ruby et Ruby on Rails, j'ai été confronté au problème d'installation. En effet, sur la version 10.04 de Ubuntu, difficile d'installer la dernière version de cet environnement. Voici la recette qui a marché pour moi.

Installer Ruby 1.9.2


La version de l'environnement Ruby disponible dans les dépôts n'est pas assez récente pour les étapes à venir, donc il faut désinstaller tout ce que avoir été installé auparavant pour repartir sur une base saine.

Installation de RVM


Ruby enVironnement Manager et un script permettant d'installer et gérer plusieurs version de l'environnement Ruby. Son installation se fait en lançant la commande suivante:

$> sudo bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)

Installation de Ruby 1.9.3


En utilisant RVM, on peut installer Ruby via la commande suivante:

$> sudo rvm install ruby-1.9.2

Finalisation de l'installation


Il faut maintenant ajouter l'environnement RVM via le fichier .bashrc
Remplacer :

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

Par :

# If not running interactively, don't do anything
if [[ -n "$PS1" ]] ; then
[[ -s "/usr/local/rvm/scripts/rvm" ]] && source "/usr/local/rvm/scripts/rvm" # This loads RVM into a shell session.
fi

Positionner la version courante de Ruby


Il faut maintenant indiquer la version courante de Ruby à employer via la commande suivante :

$> sudo rvm --default ruby-1.9.2

Installer Rails 3


Ruby est accompagner d'un utilitaire gem. Ce dernier est un gestionnaire de paquet propre à Ruby. Ainsi, un logiciel est empaqueter dans une archive (un gem) puis mise à disposition et peut être installer via l'utilitaire du même nom. Nous allons installer Rails via cette commande:

$> sudo gem install rails

J'utiliserai une base de données MySQL par la suite, j'installe donc les drivers adéquats:

$> sudo apt-get install libmysqlclient16-dev
$> sudo gem install mysql
$> sudo gem install mysql2

Tester l'installation


Rails permet de créer un projet via la commande suivante:

$> rails new /path/to/project -d mysql

Pour lancer le serveur et tester l'application:

$> rails server

Il ne reste plus qu'à naviguer vers la page http://localhost:3000/

Conclusion


L'installation, bien que plus compliquée qu'un simple «apt-get install», reste assez bien outillée. D'autres ticket viendrons tracer mes premiers pas sur ce nouveau continent.

jeudi 21 avril 2011

JConsole & Weblogic

Un article qui m'a été fort utile pour me connecter sur des instances weblogic afin d'en visualiser les entrailles à distance.
Tout est là.

vendredi 15 avril 2011

L'effaceur

Comment tuer certains processus lancés par un utilisateur ?

$> ps -aef | grep ^<USERID> | grep <PROCNAME> | awk '{ print "kill -9 " $2 }' | sh

Où <USERID> représente l'identifiant de l'utilisateur ayant lancé ses processus, et <PROCNAME> représente le nom du processus lancé par <USERID>.

Supprimez le dernier tube pour voir les commande que vous auriez lancées «manuellement» pour faire la même chose.

vendredi 1 avril 2011

«Git, dessine moi un mouton»

Il y a quelques mois je découvrai Git, un gestionnaire de sources distribué dont la réputation croît en ce moment. Ayant déjà vécu la montée en puissance de Subversion au détriment de CVS, je voulais me rendre compte des qualités/défauts du nouveau challenger.

Une nouvelle philosophie


Git fait partie de la famille des contrôleurs de sources distribués. Cela se traduit principalement par le fait que chaque membre d'une équipe collaborant à un même projet est en possession d'un dépôt contenant l'ensemble de l'historique du projet. Les publications se font ainsi en local, ne nécessitant alors aucune connectivité réseau. Seul les échanges entre dépôts se font via le réseau.
Une première question vient alors à l'esprit : Si tout le monde est un dépôt, d'où provient la version de l'application à livrer ? Plusieurs organisation sont possibles à ce sujet:

  • Une organisation par cercle de confiance, comme par exemple pour le développement du noyau Linux, où un membre de l'équipe représente le dépôt de référence que tout le monde utilise pour compiler sa version du noyau, alimenté par les dépôts de plusieurs lieutenant possédant chacun un dépôt alimenté par des dépôts considérés par ses derniers comme étant de confiance.

  • L'outil est suffisament souple pour reproduire les mécanismes des dépôts centralisés. Un dépôt sur un serveur sert de référence, utilisé par exemple par un automate de compilation permettant d'assurer une intégration continue des sources du projet, les développeurs de l'équipe déposent alors des lots de publication sur ce dépôts lors de chaque instruction git push

  • et bien d'autres ...


Une autre question suvient : Comment se joindre à un projet ? Il suffit tout simplement de cloner un dépôt avec la commande git clone /jango/fet.git et vous voilà avec un clone tout neuf, puis de commencer les développement. C'est un peu comme si chaque développeur avait forker le projet afin de produire une version. L'ensemble des développements publiés dans un dépôt représente alors un patch à appliquer aux autres dépôts pour contribuer au développement.

Une migration en douceur


Lors de la migration de CVS vers Svn, un utilitaire fournit (cvs2svn) permettait de migrer un dépôt CVS en dépôt Subversion tout en conservant l'historique. Une action one shot qui implique de synchroniser l'ensemble des membres de l'équipe utilisant le dit dépôt vers le nouvel outil.
Permier avantage de Git sur Svn : git-svn. Cet utilitaire fait la même chose que le précédent, c'est-à-dire de constituer un dépôt Git à partir d'un dépôt Subverion tout en conservant l'historique via la commande git svn clone. Mais il va plus loin en conservant une connectivité avec le dépôt Subversion lui servant d'origine. Ainsi le dépôt git et maintenu à jour en continu avec la commande git svn rebase.
Cela va encore plus loin en permettant de publier les commit effectués dans le dépôt Git vers le dépôt Subverion via la commande git svn dcommit. De cette manière, un développeur peut se mettre à utiliser Git tout en continuant de collaborer à un projet dont le référentiel de source est Subversion (ce que je fit pendant plusieurs mois).

Un bénéfice au quotidien


«Git est un nouvel outil!
- d'accord
-Il est plus fashion!
- OK, mais encors?»

«Wiiiilsooooon !!!!»


On peut aussi tout simplement utilser Git en mode seul au monde pour historiser tout ce qui se trouve sur un ordinateur. La création d'un dépôt se fait avec la commande git init, puis ont ajoute les fichiers à historiser via la commande git add monprecieux.fic puis on plublie les modification via la commande git commit. Pas besoin d'un serveur où d'un dépôt à créer et/ou administrer. C'est simple et efficace et l'on peut s'en servir pour maintenir l'historique des fichiers de configuration sur un serveur (<troll>bein oui un serveur Linux/UNIX, comment on ferait pour historiser les clics droits sous Windows®</troll>) et tout projet sandbox commencé mais jamais terminé. Je m'en sert également pour réaliser des kit de démarrage afin de savoir toutes les étapes que j'ai franchies et ainsi produire une documentation exhaustives.

«Release Early, Release Often»


Ce principe de livraison fréquente peut être appliquer au niveau du développeur qui va pouvoir publier fréquement ces modifications induisant ainsi une meilleur qualité de ces publications (fini le fichiers qui manquent, les modifications oubliées dans un coin ne sachant pas si elles concernaient la tâhes en cours, ...). Chaque modification faisant progresser le code d'un état stable à un autre (bien qu'intermédiaire) doit faire peut faire l'objet d'un commit ce qui n'est pas envisageable sur un projet centralisé.

Un build incassable


Cette capacité à pouvoir publier sans retenue permet la mise en oeuvre du build incassable. Dans la mesure où seul le dépôt local est touché par ces modifications une sécurité m'est offerte en clonant mon propre dépôt dans un endroit de mon ordinateur et en construisant mon projet à partir de ce clone afin de constater la fiabilité des mes développements avant le partage de mes travaux.

«cou roucoucou roucoucou stach stach»


Il y a des moments où le travail suit son cours, le soleil est radieux, la journée a bien commencé, quand tout à coup le ciel s'obscurci, un bug en prod doit être fixé de toute urgence sinon c'est la fin du monde. Avec SVN il me fallait alors créer une nouvelle copie de travail (ce qui peut être long sur de très gros projet) puis la supprimer une fois l'urgence traitée. Où alors de créer un patch, annuler toutes mes modifications en cours puis appliquer le patch après la tempête.
La commande git stash permet ainsi de mettre de côté (to stash = planquer) les modifications en cours pour repartir d'une version clean de la copie de travail puis de reprendre les travaux au moyen de la commande git stash pop, tout ceci avec une efficacité sans précédent pour moi.

Conclusion


En conclusion, je n'ai pas listé beaucoup de défauts (aucun pour être précis) mais je noterai tout de même l'effort à produire pour changer d'habitude et notamment le changement de philosophie induit par la distribution des dépôts.
Je m'y met de plus en plus serieusement ce qui implque d'autres publications à l'avenir sur cet outil qui fait maintenant parti de mes meilleurs amis en tant que développeur.

jeudi 31 mars 2011

Une messagerie instantannée ... en JEE 6

Durant ma formation universitaire il m'a été donné plusieurs fois l'occasion de programmer des applications orientées réseau.
Tout d'abord en C afin d'apprendre l'utilisation des Sockets et d'illustrer les cours de réseaux, puis par la suite en Java pour mettre en oeuvre la programmation multi-Thread avec une interface graphique basique.
Le thème récurrent de tous ces exercices est le cas d'une messagerie instantanée permettant l'échange de messages entre différents interlocuteurs.
On ne change pas une technique qui gagne, je remet une pièce dans la machine pour illustrer cette fois une implémentation en ... XHTML/JavaScript reposant sur un serveur Java et une Servlet asynchrone.

La théorie


En théorie rien de bien compliqué:

Le client

Une partie de l'application responsable de l'envoi du commentaire de chaque utilisateur à destination du serveur et en même temps à l'écoute des commentaires en provenance du serveur

Le serveur

L'autre partie de l'application recevant les commentaires de chaque client et renvoyant ces commentaires à l'ensemble des clients connectés


La pratique


En C puis en Java, la technique est assez similaire et consiste à ouvrir une socket client->serveur permettant l'envoi des commentaires ainsi qu'une autre socket serveur->client permettant la restitution des commentaires à chaque client. On pouvait envisagé également l'utilisation des IP de broadcast pour simplifier l'architecture. Mais en JEE, comment implémenter tout cela ?

Le serveur


Une solution de base est trouvée tout simplement avec l'apparition des traitements asynchrones des requêtes proposé par les Servlets Java à partir de la spécification 3.0. Cette Servlet traitera donc de manière asynchrone les requêtes de type GET pour mettre les clients en attente de commentaires, ces derniers étant transmis à la même Servlet par des requête de type POST.

La réception de message


Pour recevoir les messages, la Servlet propose une implémentation de la méthode doPost(...), cette dernière recevant le commentaire d'un client le transmet aussitôt à l'ensemble des clients en écoute via une méthode fireMessage(...) décrite plus bas.

/**
* On répond au POST en transmettant les informations à tous les clients en attente.
*/
@Override
protected void doPost( HttpServletRequest req, HttpServletResponse resp )
throws ServletException, IOException
{
String nickname = req.getParameter( "nickname" );
String message = req.getParameter( "message" );
fireMessage( new PostData( nickname, message ) );
}

Des clients en écoute ?


Et oui, ces derniers on quand à eux fait un appel GET et doivent être en attente, c'est donc la méthode doGet(...) qui fait cela en appelant la méthode startAsync sur l'objet request transmis à la Servlet. Cet appel retourne une instance de AsyncContext que nous allons conserver précieusement dans une file d'attente.

/**
* Un client se tient informé des nouveaux messages, on le place en file d'attente.
*/
@Override
protected void doGet( HttpServletRequest req, HttpServletResponse resp )
throws ServletException, IOException
{
if ( req.isAsyncSupported() )
{
AsyncContext context = req.startAsync();
context.setTimeout( 1 * MINUTE );
contexts.add( context );
}
else
{
log( "Les requêtes asynchrones ne sont pas supportées, dommage!" );
}
}

La notification des clients


Il est temps maintenant de prévenir les clients en attente du dernier message reçu:

/*
* Envoi les message aux clients en attente.
*/
private void fireMessage( PostData postData )
throws IOException
{
while ( !contexts.isEmpty() )
{
AsyncContext context = contexts.poll();
ServletResponse response = context.getResponse();
assert response != null;
response.setContentType( "text/json" );
Writer out = response.getWriter();
String json = postData.toJSON();
out.write( json );
out.flush();
context.complete();
}
}

Dans cette méthode comme dans la méthode doGet(...), on fait appel à un membre nommé contexts. J'ai choisit ici de mettre les clients dans une file d'attente protégée contre les accès concurrents.

/* Ensemble des requétes asynchrones en attente de réponse. */
private Queue<AsyncContext> contexts = new ConcurrentLinkedQueue<AsyncContext>();

Le client


Maintenant que notre serveur est prêt, il convient d'implémenter un client. Pour respecter l'emploi des fondamentaux de cet article, c'est donc une page Web en XHTML/JavaScript qui fera office de client au moyen du navigateur. Cette page devra être capable d'envoyer les commentaires de l'utilisateur et en même temps d'afficher les commentaires reçus.
C'est une technique connu sous le nom d'AJAX qui permettra de gérer l'envoi / reception de messages. J'ai choisit d'utiliser jQuery afin de simplifier son utilisation mais il aurait été tout à fait possible de coder tout cela avec une belle XmlHTTPRequest.

L'envoi d'un commentaire


Pour saisir le commentaire, rien de mieux qu'un petit formulaire.

<div>
<label for="nickname">Pseudo</label><input id="nickname" type="text" value="" tabindex="1" />
<input id="send" type="button" value="Envoyer" tabindex="3" />
</div>
<textarea id="message" rows="5" cols="40" tabindex="2"></textarea>

Puis un peu de JavaScript avec jQuery.

$(document).ready(function() {//onload de la page
$("#send").click(function() {//onclick sur le bouton send
sendMessage($("#nickname").val(), $("#message").val());
});
});
function sendMessage(nickname, message) {
$.post("chat", {
nickname : nickname,
message : message
}).success(function() {
$("#message").val('');
}).error(function() {
alert('NOGO : failure sendind data');
});
}

La réception d'un commentaire


La réception d'un commentaire commence dès le chargement de la page.

$(document).ready(function() {//onload de la page
...
tryToPull();
});
function tryToPull() {
$.ajax({//long polling
url : "chat"
}).success(function(data) {
addMessage(data);
}).error(function() {
alert('Error!');
});
}
function addMessage(data) {
try {
if (data.nickname && data.message) {
$("#messages").html("
" + data.nickname + "
" + data.message + "
");
}
tryToPull();
} catch (e) {
alert(e.message);
}
}

Les données reçues sont au format JSON comme indiqué par le code de la Servlet. jQuery reçoit donc un flux JSON qui devient un objet JavaScript, le fameux data de la méthode addMessage(...)

Conclusion


Une fois l'application chargée dans Tomcat 7 : TADA ! Le chat a encore frappé, voici une illustration de l'utilisation des Servlet 3.0 pour le traitement asynchrone des requêtes.
Remarque n°1: Il faut utiliser 2 navigateurs différents pour être sûr d'avoir 2 clients.
Remarque n°2: Je n'ai pas écrit de fichier web.xml car c'est devenu optionnel avec JEE 6.

jeudi 10 février 2011

MVP4G Spring et GWT

Parmis les plateformes de développement de clients riches (chaînon manquant entre client lourd et client léger), 2 sont en forte concurrence : Google Web Toolkit(alias GWT) et Adobe Flex.

Le but ici n'est pas de traiter qui est le meilleur, mais de faire un focus sur la mise en oeuvre de GWT dans le cadre d'une application métier complexe et critique. Dans ce sens, la grande quantité de composants dans l'application ainsi qu'une maintenance évolutive constante ont conduit à utiliser un framework permettant maîtriser les efforts de maintenance. Ce choix c'est porté sur le framework MVP4G.

Voici comment développer une application GWT reposant sur ce framework.

Instant zéro : les outils


Les outils utilisés dans le cadre de cette démarche sont:

Maven 3

Le couteau suisse par excellence

Eclipse Helios

Un des plus puissants environnements de développement gratuits

GWT

Le kit de développement GWT ainsi que le plugin pour Eclipse


Je ne détaillerai pas ici l'installation de ces outils, on se rendra sur les sites dédiés pour en savoir plus.

The Bigbang Theory


Boom!


Maintenant que les outils sont prêts, l'heure est venue de créer notre projet. Nous utiliserons maven qui dispose d'un plugin permettant de gérer les projets GWT.

> mvn archetype:generate

Cette commande vous propose de choisir le modèle de projet à générer parmi la pléthore d’archétypes disponibles. Il faut chercher dans cette liste le type de projet "gwt-maven-plugin" qui porte le numéro 235 lors de l'écriture de ces lignes. On choisira par la suite le numéro de version à utiliser (la dernière par défaut) puis l'assistant va demander quelques informations sur le projet : groupId, artifactId, version, ... et le nom du module GWT. Ce dernier donnera une classe <NOM_DU_MODULE>.java héritant de EntryPoint puis un ficher <NOM_DU_MODULE>.gwt.xml.

Premier run dans Eclipse


Nous venons de mettre au monde notre projet en quelques secondes, maintenant il convient de lui faire faire ses premiers pas. Pour cela on se rendra dans le dossier construit par maven que nous noterons par la suite <artifactId>.

> cd <artifactId>
> mvn eclipse:eclipse

On importera le projet dans Eclipse via Import ... / Existing project.
Il est possible que le fichier pom.xml ne soit pas tout à fait propre, j'ai entre-autre purifié son contenu pour prendre en compte la version Java 6 des sources en lieu et place de Java 5, puis customisé un peu la configuration du plugin maven-eclipse-plugin avant de relancer la commande précédente pour obtenir un projet "propre".
Pour lancer le projet dans Eclipse, un simple clic droit Run as ... / Web application et voila notre projet qui tourne. Il s'agit du projet de base créé également lorsque l'on utilise l'assistant Eclipse, le site officiel GWT en dit plus sur ce projet dans le guide du débutant.

Un peu de Spring


Le projet met en oeuvre un service GWT RPC afin d'illustrer ce fonctionnement. Ces services RPC sont des servlets à déclarer dans le ficher web.xml. Dans le cadre d'une application à grande échelle, une grande quantité de services vont être développés afin de convenir aux spécifications du métier. Dans ce sens il convient d'améliorer la productivité sur le développement et la mise en oeuvre de ces services. Le plus simple étant de laisser le framework Spring instancier ces services et l'on disposera alors d'une seule et unique servlet dispatchant les appels RPC au beans construits pour assurer l'implémentation de ces services.

"Dit à Maven de dire à Eclipse ..."


Première étape, on modifie le fichier pom.xml pour ajouter la dépendance vers les bibliothèques nécessaires:

<project>
...
<properties>
<springVersion>2.5.6.SEC02</springVersion>
<properties>
...
<dependencies>
...
<!-- Spring 4 GWT -->
<dependency>
<groupId>com.google.code</groupId>
<artifactId>spring4gwt</artifactId>
<version>0.0.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springVersion}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
...
</dependencies>
...
</project>

On met à jour le projet via la commande magique pour ajouter automatiquement les librairies indiquées dans Eclipse:

> mvn eclipse:eclipse

... puis Refresh ou F5 dans Eclipse et c'est parti!

Refactor


Notre projet n'a pas beaucoup changé, il est juste plus gros car embarque désormais plusieurs JAR supplémentaires.

applicationContext.xml


On ajoutera un fichier de description des beans Spring en prenant soin de faire appel à une fonction pratique : le scan automatique de composants. Ce fichier sera déposé dans le dossier src/main/resources du projet:

<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:component-scan
base-package="PAQUETAGE_DE_BASE_DE_MON_PROJET.server" />
</beans>

web.xml


Nous allons mettre à jour le descripteur de l'application afin de remplacer la Servlet existante par une Servlet un peu plus dynamique. Supprimer les lignes suivantes du fichier web.xml se trouvant dans src/main/webapp/WEB-INF/

<web-app>
...

<!-- Servlets -->
<servlet>
<servlet-name>greetServlet</servlet-name>
<servlet-class>PAQUETAGE_DE_BASE_DE_MON_PROJET.server.GreetingServiceImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>greetServlet</servlet-name>
<url-pattern>/NOM_DE_MON_MODULE_GWT/greet</url-pattern>
</servlet-mapping>

...
</webapp>

... puis nous ajouterons les lignes suivantes:

<webapp>
...
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- SpringGwt remote service servlet -->
<servlet>
<servlet-name>springGwtRemoteServiceServlet</servlet-name>
<servlet-class>org.spring4gwt.server.SpringGwtRemoteServiceServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>springGwtRemoteServiceServlet</servlet-name>
<url-pattern>/NOM_DE_MON_MODULE_GWT/services/*</url-pattern>
</servlet-mapping>

<!-- Spring servlet filter -->
<filter>
<filter-name>springRequestFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>springRequestFilter</filter-name>
<servlet-name>springGwtRemoteServiceServlet</servlet-name>
</filter-mapping>
...
</webapp>

"Sinon le refactor c'est quand ?"


Nous allons mettre à jour le code du service en commençant par son interface. L'annotation permettant à GWT de connaître une partie de l'URL de ce dernier doit être mise à jour. Dans le fichier GreetingService.java l'annotation prendra la valeur suivante:

@RemoteServiceRelativePath("services/greet")

Le changement majeur sera pour l'implémentation du service. Jusqu'à présent, la classe GreetingServiceImpl héritait de RemoteServiceServlet, une classe propre à GWT. Nous allons briser cet héritage transformant ainsi la classe en un java bean classique.

...
@Service("greet")
public class GreetingServiceImpl
implements GreetingService {
...
}

Notre classe ne compile plus car elle faisait appel à des méthodes de RemoteServiceServlet afin d'accéder à des propriétés de la requête. Voici le nouveau code que nous allons mettre en place pour palier cette lacune:

...
HttpServletRequest request = ((ServletRequestAttributes)(RequestContextHolder.getRequestAttributes())).getRequest();
String serverInfo = request.getSession().getServletContext().getServerInfo();
String userAgent = request.getHeader("User-Agent");
...

Le refactor est maintenant terminé, on relance le serveur et on constate que l'application fonctionne comme avant. Dingue!

MVP4G


Nous rentrons maintenant dans le vif du sujet, nous pouvons ajouter autant de services que nous voulons sans trop d'efforts, faisons de même pour l'écran.

"Dit aussi à Maven de dire à Eclipse ..."


Nous ajoutons maintenant la dépendances nécessaire à l'utilisation du framework MVP4G dans le fichier pom.xml:

<project>
...
<dependencies>
...
<!-- MVP design pattern 4 GWT -->
<dependency>
<groupId>com.googlecode.mvp4g</groupId>
<artifactId>mvp4g</artifactId>
<version>1.2.0</version>
</dependency>
...
</dependencies>
...
</project>

... puis la commande magique (voir plus haut), maintenant nous sommes prêts.

Definition du module GWT


Nous ajouterons un lien vers le module GWT MVP4G via le descripteur de notre module GWT NOM_DE_NOM_MODULE_GWT.gwt.xml:

<module>
...
<!-- Inherit the Mvp4gModule stuff. -->
<inherits name='com.mvp4g.Mvp4gModule' />
...
</module>

Le présentateur


Pierre angulaire du pattern MVP, nous allons produire un présentateur reproduisant le comportement actuel de notre application :

/**
* Presenter de salutation.
*/
@Presenter(view = GreetView.class)
public class GreetPresenter
extends LazyPresenter<IGreetView, RootEventBus>
{
/**
* Specification d'une vue servant a la salutation.
*/
public static interface IGreetView
extends LazyView
{
/** Composant permettant la saisie du nom. */
HasValue<String> getUsername();

/** Indication d'une erreur éventuelle. */
HasText getErrorLabel();

/** Bouton soumettre. */
HasClickHandlers getButtonSend();

/** Désactive le bouton de soumission. */
void disableButtonSend();

/** Active le bouton de soumission. */
void enableButtonSend();
}

@Override
public void createPresenter() {
}

@Override
public void bindView() {
view.getButtonSend().addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
final String username = view.getUsername().getValue();
view.getErrorLabel().setText("");
if (!FieldVerifier.isValidName(username)) {
view.getErrorLabel().setText("Please enter at least four characters");
} else {
view.disableButtonSend();
greetService.greetServer(username, new AsyncCallback<String>() {
@Override
public void onSuccess(String result) {
greetSucceed(username, result);
}

@Override
public void onFailure(Throwable caught) {
greetFailed(username, caught);
}
});
}
}
});
}

/**
* @see RootEventBus#start()
*/
public void onStart() {
// Nothing special to do here.
}

protected void greetSucceed(String username, String result) {
final Label textToServerLabel = new Label(username);
final HTML serverResponseLabel = new HTML(result);
VerticalPanel widget = new VerticalPanel();
widget.addStyleName("dialogVPanel");
widget.add(new HTML("<b>Sending name to the server:</b>"));
widget.add(textToServerLabel);
widget.add(new HTML("<br><b>Server replies:</b>"));
widget.add(serverResponseLabel);
widget.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
showPopup("Remote Procedure Call", widget);
}

protected void greetFailed(String username, Throwable caught) {
final Label textToServerLabel = new Label(username);
final HTML serverResponseLabel = new HTML("An error occurred while "
"attempting to contact the server. Please check your network " "connection and try again.");
serverResponseLabel.addStyleName("serverResponseLabelError");
VerticalPanel widget = new VerticalPanel();
widget.addStyleName("dialogVPanel");
widget.add(new HTML("<b>Sending name to the server:</b>"));
widget.add(textToServerLabel);
widget.add(new HTML("<br><b>Server replies:</b>"));
widget.add(serverResponseLabel);
widget.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
showPopup("Remote Procedure Call - Failure", widget);
}

private void showPopup(String title, Widget widget) {
final DialogBox popup = new DialogBox();
popup.setText(title);
VerticalPanel container = new VerticalPanel();
container.add(widget);
Button closeButton = new Button("Close");
closeButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
popup.hide();
view.enableButtonSend();
}
});
container.add(closeButton);
popup.add(container);
popup.center();
}

@Inject
private GreetingServiceAsync greetService;
}

La vue


Notre code ne compile pas pour le moment car il nous manque le complice du présentateur : la vue. Nous allons maintenant lui donner vie.

/**
* Sample Greet view.
*/
public class GreetView
extends Composite
implements IGreetView
{
@Override
public HasValue<String> getUsername() {
return username;
}

@Override
public HasText getErrorLabel() {
return errorLabel;
}

@Override
public HasClickHandlers getButtonSend() {
return buttonSend;
}

@Override
public void disableButtonSend() {
buttonSend.setEnabled(false);
}

@Override
public void enableButtonSend() {
buttonSend.setEnabled(true);
}

/**
* @see com.mvp4g.client.view.LazyView#createView()
*/
@Override
public void createView() {
DockLayoutPanel container = new DockLayoutPanel(Unit.PX);
Widget title = new HTML("<h1>Web Application Starter Project</h1>");
container.addNorth(title, 140);
FlowPanel content = new FlowPanel();
username = new TextBox();
content.add(username);
buttonSend = new Button(messages.sendButton());
content.add(buttonSend);
errorLabel = new Label();
errorLabel.addStyleName("serverResponseLabelError");
content.add(errorLabel);
content.addStyleName("content");
container.add(content);
this.initWidget(container);
this.addStyleName("greetView");
}

private TextBox username;

private Label errorLabel;

private Button buttonSend;

@Inject
private Messages messages;
}

Le car ... non le bus!


La colonne vertébrale de notre application est un bus d’évènements permettant de connecter entre eux l'ensemble des composants de notre application à la manière d'une colonne vertébrale.

/**
* Bus d'événement global à l'ensemble de l'application.
*/
@Events(startView = GreetView.class)
public interface RootEventBus
extends EventBus
{
/**
* Premier événnement lancé au démarrage de l'application.
*/
@Start
@Event(handlers=GreetPresenter.class)
void start();
}

"Et maintenant on refactor ?"


Le point d'entrée d'une application est la classe qui étend EntryPoint, celle automatiquement générée lors de la création du projet concentre l'ensemble du comportement de l'application, ce que nous avons distribué dans le présentateur et la vue. Nous pouvons donc supprimer l'ensemble du contenu initial pour mettre le code suivant :

/**
* Point d'entrée du module GWT.
*/
public class NomDeMonModule
implements EntryPoint
{
/**
* This is the entry point method.
*/
public void onModuleLoad() {
// Instanciation du module racine MVP4G.
Mvp4gModule module = (Mvp4gModule) GWT.create(Mvp4gModule.class);
module.createAndStartModule();
// La vue du module principal est positionné dans la page.
RootLayoutPanel.get().add((Widget)module.getStartView());
}
}

Cette classe ne bougera plus à l'avenir. Avant de lancer l'application et constater les changements, il conviendra de nettoyer le fichier HTML en supprimant le contenu qui n'est plus utile, c'est à dire tout depuis le tag H1 jusqu'à BODY.

Bazinga!


Nous venons de migrer une simple application GWT vers un socle technique lui permettant d'accueillir à l'avenir une grande quantité de services RPC ainsi qu'une interface riche, complexe et souple.

mardi 8 février 2011

Il faut lire!

Travailler sur des projets informatique où des dizaines de fichiers sources sont manipulés sur de longue durée et par de nombreuse personnes n'est plus envisageable sans un outil permettant d'assurer un historique des modifications.

Dans la galaxie d'outils permettant d'assurer la gestion des fichiers sources brille une étoile un peu particulière: Git. Après avoir utilisé Clearcase (de Rational) nécessitant un lourd workflow dans la gestion des sources, puis Subversion offrant une liberté de mouvement totale quant à la façon de traiter les fichiers sources, voici celui qui permet le mieux a mon sens la liberté absolue sans contraintes de connectivité.

J'en ferai le détail dans d'autres ticket à venir mais en attendant voilà un bouquin qu'il est bien pour lire.