Shared posts

23 Aug 11:22

Les tests unitaires paramétriques avec JUnitParams, une alternative à Junit Parameterized

by Olivier Pitton
Lorsque l'on souhaite tester un même comportement sur différentes données, on peut soit : Développer plusieurs méthodes de tests qui vérifient le même code avec des entrées différentes ; Utiliser une méthode de test paramétrique. Les tests paramétriques permettent d'exécuter une même méthode de test sur des données différentes (les paramètres).
30 Jul 06:36

Les personnes utilisant Maven et Ant ne seront jamais d'accord sur rien. Faites avec !

by Olivier Pitton
Tous les 3 mois environ, vous verrez un nouveau article apparaitre de nul part écrit par un amoureux de Ant (parfois de Gradle ou Rake) expliquant à quel point Maven est horrible. S'il vous plait, quel age avez-vous ? J'ai utilisé les deux et je préfère Maven.
19 Jul 06:44

Java 8 – What’s new ? – 1/3 – Project Lambda

by Yohan BESCHI
Java 8 - What’s new ? - 1/3 - Project Lambda

Après avoir attendu presque 5 ans la sortie de Java 7, Oracle a annoncé fin 2011 un planning de livraison des futures versions de Java. Il n’aura pas fallu attendre longtemps pour qu’il remette en cause ce planning. Les itérations...
19 Jul 06:44

Java 8 – What’s new ? – 2/3 – Date and Time

by Yohan BESCHI
Java 8 - What’s new ? - 2/3 - Date and Time

Dans la partie précédente, nous avons vu tout ce qu’apporte le Projet Lambda à Java. Mais Java 8 ce n’est pas seulement les lambdas et un zeste de programmation fonctionnelle. C’est aussi de nombreuses améliorations tout aussi révolutionnaires. Il aura...
19 Jul 06:43

Mockito ou comment faciliter l’écriture de tests unitaires

by Aurélien Garnier
Mockito ou comment faciliter l'écriture de tests unitaires

Mockito est un framework Java, permettant de mocker ou espionner des objets, simuler et vérifier des comportements, ou encore simplifier l’écriture de tests unitaires. Pour ceux qui veulent (re)trouver du plaisir à écrire des TU, cet article est fait pour vous....
13 Jun 20:22

Surveillance numérique : comment les Etats-Unis peuvent-ils redorer leur image ?

by vidberg
27 May 11:44

Introduction à Lean Startup

by Yannick Grenzinger

Commençons par un constat : 9 produits sur 10 échouent (et souvent la Startup qui porte un de ces produits). C’est en partant de ce bilan qu’Éric Ries publie en septembre 2011 The Lean Startup: How Today’s Entrepreneurs Use Continuous Innovation to Create Radically Successful Businesses qui devient un succès mondial et un véritable mouvement avec ses conférences et ses nombreux meetups (par exemple Lean Startup France).

L’objectif de Lean Startup, dans la continuité des méthodes Agiles, est de réduire le cycle de développement d’un produit afin de réduire les risques d’échec, de mesurer le progrès et d’obtenir de précieux feedbacks des clients, et cela de manière régulière. De cette façon, les entreprises, en particulier les startups, peuvent concevoir leurs produits ou services pour répondre aux demandes de leur clientèle sans nécessiter un important budget de démarrage ou de coûteux lancements de produits.

Si on se permet la remarque, cette méthode va encore plus loin que les méthodes Agiles comme Scrum et remet en cause les méthodes de réalisation d’un produit traditionnellement présentes dans les entreprises fondées sur des mythes comme :

  • Nous savons ce que le client veut,
  • Nous pouvons prédire le futur,
  • Avancer selon le plan c’est progresser.

À l’instar des préceptes de Lean Management, la philosophie Lean Startup cherche à éliminer les gaspillages et accroître la création de valeur pendant la phase de développement du produit. L’objectif est d’obtenir les meilleures chances de réussite sans nécessiter un important financement, des Business Plan élaborés ou un produit parfait. Eric Ries estime que les retours des clients au cours du développement du produit font partie intégrante du processus Lean Startup, et veille à ne pas investir trop de temps à concevoir des fonctions ou des services que les utilisateurs ne veulent potentiellement pas.

Éric Ries s’est fortement inspiré de l’ouvrage de Donald Reinertsen le « The Principle of Product Development Flow », du  »Customer Development » de Steve Blank, des méthodes de Design UX ou encore du monde Agile pour créer une approche de lancement de produits qui repose principalement sur :

  • la validation des hypothèses
  • des expérimentations scientifiques,
  • un apprentissage constant,
  • des déploiements en production les plus fréquents possible.

Lean Startup est vue comme une méthode d’amélioration continue du produit en trois étapes :  construire – mesurer – apprendre

WF_LEAN_Startup.png

Construire

« Aucun business plan ne survit au premier contact avec le client » – Steve Blank

Tout produit est un ensemble d’hypothèses qu’il va falloir valider de façon qualitative et quantitative sur le terrain. Ces hypothèses, en particulier les plus risquées, doivent être validées par un MVP ou Minimum Viable Product.

Ce MVP est la plus simple version d’un nouveau produit. Il permet à une équipe d’apprendre au maximum, avec une validation des clients et avec le moindre effort. Il y a différents types de MVP (‘magicien d’Oz’, concierge, test papier, pré-vente, Landing Page avec un appel à action, vidéo) mais tous ont pour objectif de recueillir des retours concrets des utilisateurs afin de trouver la meilleure adaptation au marché pour le produit.

Si cette étape en est rendue à un vrai logiciel, il faut utiliser au maximum les techniques de développement modernes en découpant en itérations courtes et efficaces. Cela va du déploiement sur le Cloud, de la mise en place de l’intégration continue jusqu’au déploiement continu en passant bien sûr par les méthodes d’organisation projet Agile telles que Scrum ou Lean Software Management avec les pratiques Kanban.

Mesurer

« Les clients ne s’intéressent pas à vos solutions, ils se soucient de leurs problèmes » – Dave McClure

Dans Lean Startup, la validation des hypothèses se fait de façon scientifique avec des méthodes aussi bien qualitatives que quantitatives. On peut même aller jusqu’à dire qu’il y a une certaine obsession de la mesure !

Les méthodes qualitatives reprennent les outils connus de l’expérience utilisateur tel que l’interview d’utilisateurs. Il s’agit de rencontrer les utilisateurs afin qu’ils utilisent le produit tout en observant leurs réactions ou encore en leur posant des questions.

Les méthodes quantitatives telles que les méthodes de type A/B testing sont encore plus utilisées, associées à des outils d’analyse du trafic et de l’activité comme Google Analytics. Pour créer une mesure qualitative de qualité, il faut aussi choisir les bons indicateurs clés de performance. Cela peut être le taux d’acquisition comme le nombre d’utilisateurs créant un compte sur le site ou encore l’engagement de ceux-ci en faisant le ratio entre le nombre d’utilisateurs actifs par mois et par jours.

Pour donner un exemple très simple, dans le cadre d’un MVP de type Landing Page, on peut créer deux versions du site avec deux propositions de valeur différentes. En utilisant la technique de l’A/B testing et en mesurant le nombre de personnes donnant leur email pour être informé de la sortie du produit, on peut ainsi déterminer quelle solution intéressera le plus les clients potentiels.

Apprendre

« Les startups qui réussissent sont celles qui parviennent à itérer assez de fois avant d’épuiser leurs ressources » – Éric Ries

La construction et la mesure ont un seul véritable objectif : apprendre, apprendre, apprendre ! Il faut alors utiliser au maximum l’usage de feedbacks de la clientèle afin d’adapter davantage ses produits aux besoins spécifiques de ses clients.

Lean Startup permet d’apprendre quels sont les éléments, les fonctionnalités qui apportent de la valeur ou au contraire celles qui créent du gaspillage. Mais pas seulement ! La méthode permet aussi de tester le marketing du produit ou encore son Business Model. Si les indicateurs sont bons, c’est alors le signe qu’il faut persévérer et continuer d’améliorer l’existant. Au contraire, si les indicateurs sont mauvais, il faut alors pivoter.

Le pivot est une nouvelle hypothèse stratégique qui va nécessiter un nouveau MVP. Des pivots à succès permettent de mettre le produit sur le chemin d’un business ayant une croissance durable.

Il y a plusieurs types de pivot par exemple :

  • Zoom In : ce qui était considéré comme une fonctionnalité d’un produit devient le produit dans son ensemble,
  • Zoom Out : ce qui était considéré comme le produit dans son ensemble devient une seule fonctionnalité d’un produit plus large,
  • Customer Segment : on change le type de client qu’on souhaitait adresser originalement,
  • Customer Need : on réalise que le segment de clients qu’on vise a un problème plus intéressant à résoudre que celui anticipé,
  • Platform : on change d’une application à un service ou inversement,
  • Value Capture : on change la façon dont on capture la valeur, c’est-à-dire le modèle de revenu,
  • Channel : on change la façon dont on délivre le service au client,
  • Technologique : Tout en conservant la solution apportée au client, on va utiliser une technologie complètement différente.

Cependant, il ne faut pas oublier que les expérimentations sont rarement définitives et les résultats de chaque apprentissage doivent être contrebalancés par la vision et le jugement sans mettre la tête dans le sable.

the-lean-startup_big_picture.png

Dans ce premier article, j’espère vous avoir présenté de façon claire la pratique du « Lean Startup » dont tout le monde commence à parler. C’est aussi le début d’une série qui vous présentera plus en détail certains outils de cette méthode, par exemple, le Lean Canvas.

Quelques liens:

03 May 19:00

Redécouvrons le développement web en Java avec Play! – Partie #1 : Enfin un framework web Java

by Paterne Gaye-Guingnido
Redécouvrons le développement web en Java avec Play! - Partie #1 : Enfin un framework web Java

Je voudrais vous parler d’un framework web qui fait de plus en plus parler de lui : Play! Framework. Cette présentation est axée sur l’usage de Play! par un développeur Java. Cependant il est important de garder à l’esprit que...
29 Apr 08:50

Redécouvrons le développement web en Java avec Play!

by Olivier Pitton
Je voudrais vous parler d'un framework web qui fait de plus en plus parler de lui : Play! Framework. Cette présentation est axée sur l'usage de Play! par un développeur Java. Cependant il est important de garder à l'esprit que dans Play! l'aspect important n'est pas Java mais plutôt le couple JVM + Web.
13 Apr 15:48

Product Owner … qui es-tu ?

by Alexandre Boutin

Je partage avec vous ma vision du rôle du Product Owner, tous vos feedbacks sont les bienvenus :)

Rôle du Product Owner

Le Product Owner (ou PO) est avant tout un rôle initialement définie dans la méthodologie Scrum et maintenant généralisé à l’ensemble des méthodes dites « Agile ». Ce rôle est généralement tenu par un individu unique, mais il est courant de voir également une équipe de PO assurer cette mission avec succès, sous réserve que cette équipe de PO tienne un discours commun et cohérent auprès de l’équipe de réalisation.

Le PO est un membre à part entière de l’équipe Scrum dont la responsabilité principale est de définir un produit qui apportera le maximum de valeur métier aux utilisateurs dans le temps et le budget impartis au projet.

Le Product Backlog

L’outil principal utilisé par le PO est le Product Backlog (ou PB) qui est la liste ordonnée des éléments constitutifs du produit. La priorité des éléments est définie suivant 4 caractéristiques : leur valeur métier, leur effort de réalisation, leur risque et la connaissance technique ou métier apportée par leur mise en œuvre. Chaque PO définit des règles de priorisation, basées sur 1 à 4 de ces caractéristiques, généralement valables pour la durée de réalisation d’une version (un produit se décompose en plusieurs versions).

Le PB sera réalisé par l’équipe de réalisation, itération après itération, dans le respect absolu de l’ordre positionné par le PO. Cette liste sera amendée (ajout ou suppression) en fonction des feedbacks récoltés auprès des utilisateurs à chaque itération, mais ces changements doivent survenir entre les itérations pour ne pas perturber l’équipe durant l’itération en cours.

L’évolution du Product Backlog sur la base des retours utilisateurs permet de garantir que le produit final répondra exactement aux attentes des utilisateurs. Comme le budget est généralement contraint et que chaque changement représente un coût, il est de la responsabilité du PO de discuter régulièrement avec les utilisateurs pour bien évaluer la valeur des changements demandés au regard des fonctionnalités à venir, et choisir l’un ou l’autre en fonction du modèle de valeur du produit.

L’Activité Principale du Product Owner

La création du PB et son maintien (ou « grooming » en anglais) sont les activités principales du PO, elle n’est pas facile car il lui faut récolter de nombreuses informations sur le produit :

  • Identifier les attentes des utilisateurs et les bénéfices que le produit leur apportera
  • Décrire avec le maximum de détails les utilisateurs et/ou clients du produit
  • Identifier les fonctionnalités attendues et sélectionner celles qui apportent le plus de valeurs ou de bénéfices aux utilisateurs pour définir et planifier les releases/versions du produit
  • Décrire chaque fonctionnalité retenue sous forme d’une User Story suffisamment petite pour être implémentée en 1 seule itération, sans oublier d’y associer les critères d’acceptation indispensables à sa bonne compréhension par l’équipe.
  • Comprendre les Technical Story proposées par l’équipe de réalisation (besoins non fonctionnels mais indispensable – ex : Migration de version d’une base de données) et les Bug Story (ou « bugs connus »)
  • Prioriser toutes les Story au sein du Product Backlog
  • Maintenir le Product Backlog et chercher en permanence à maximiser la valeur métier pour les utilisateurs
  • Accepter ou refuser les Story implémentées par l’équipe de réalisation

De nombreuses techniques, propres au métier du Product Owner, permettent de faciliter le travail du PO. Parmi celles-ci nous citerons « Story Mapping », « Product Box », « Impact Mapping », « Personas », « Blitz Planning » et « Remember the Future ».

Autres Activités du Product Owner

Si l’élaboration et l’évolution du Product Backlog représentent des activités primordiales et consommatrices de temps, ce ne sont pas les 2 seules activités dévolues au PO, car il ne faut pas oublier les suivantes :

  • Elaborer la Vision du Produit pour la version en cours
  • Partager cette Vision avec l’équipe de réalisation et leur communiquer pourquoi le produit est utile
  • Montrer aux décideurs / parties prenantes que cette vision est dans la ligne de la stratégie de l’entreprise
  • Communiquer sur l’avancement de la réalisation du produit auprès du management et des utilisateurs
  • Récolter les feedbacks des utilisateurs
  • Répondre aux demandes de clarification émises par l’équipe de réalisation sur les Story en cours de réalisation durant l’itération.
  • Contribuer aux réunions Scrum avec l’équipe de réalisation et le Scrum Master
  • Réaliser ou organiser le déroulement des Tests Utilisateurs/Métier de la version
  • Evaluer le fonctionnement des versions précédentes mises en production et en cours d’utilisation
  • Mener une réflexion stratégique préparatoire des versions à venir

Et même parfois

  • Assurer la formation des utilisateurs
  • Promouvoir le produit auprès d’autres instances (Directions métier, Filiales …)
  • Evaluer les opportunités du marché
  • Vendre le produit à des clients et ramener des commandes fermes !

Un Rôle à Temps partiel ?

La liste des activités du Product Owner décrites ci-dessus amène naturellement à la conclusion que le rôle de PO nécessite un investissement important qui conditionne très fortement le succès du produit. Cette charge de travail conséquente justifie souvent la mise en œuvre d’une Equipe PO ou plusieurs individus assurent en commun le rôle unique de Product Owner du produit.

Une cause d’échec des méthodes Agiles régulièrement identifiée est une disponibilité insuffisante du Product Owner. L’échec peut se caractériser par la réalisation d’un produit qui ne répond pas aux attentes des utilisateurs (absence de feedbacks), par le déploiement d’un produit trop complexe (orientation trop technique) ou par un périmètre incomplet et  non utilisable (absence de modèle de valeur et de priorisation).

Product Owner : Je veux me Former !

Suivre une formation spécifique pour Product Owner est une très bonne façon pour commencer à apprendre les différentes, techniques de définition de produit, de rédaction de Story et d’élaboration du Product Backlog.

Je propose depuis plusieurs années maintenant une formation spécifique pour Product Owner sur 2 jours qui est largement plébiscité par les participants qui l’ont suivi (Prochaine session : 6 et 7 mai 2013 sur Grenoble)

Cette formation est également labellisée par la Fédération Agile des Formateurs Francophones.

12 Apr 11:36

Devoxx France 2013 : Javascript pour les développeurs Java

by Pierre TEMPLIER

Lors de Devoxx France 2013 j’ai beaucoup apprécié le retour de Florian Boulay intitulé “Javascript pour les développeurs Java : quels sont les pièges à éviter ?” . Dans cette conférence, il nous a fait part des surprises qu’il a rencontré et des solutions qu’il a pu apporter.

Portée des variables

Si on ne met pas var devant une variable celle-ci est alors globale.
Solution : on met toujours var qui force une portée locale.

	var foo = 'global'
	function funct() {
		var foo = 'local';
		bar = 'global';
		console.log(foo); // 'local'
		console.log(bar); // 'global'
	}
	funct();
	console.log(foo); // 'global'
	console.log(bar); // 'global' instanciée depuis funct()

Pour une meilleure lisibilité et pour identifier facilement que l’on utilise bien des variables locales, on peut systématiquement déclarer ses variables en début de méthode.

Isolation du code

Pour éviter de déclarer des méthodes au niveau global, on les isole de la façon suivante :

(function() {
	var foo = 'hello';
	//...
})();

Cela s’appelle le module pattern.
Celui-ci permet d’éviter d’utiliser le namespace global pour la déclaration de la fonction. On évite ainsi les conflits de noms qui pourraient survenir.

Le module pattern permet aussi d’émuler le concept de classe de la manière suivante:

var module = (function() {
	var exposedObject = {},
		privateVariable = 1;

	function privateMethod() {
		// méthode privée
	}
	exposedObject.publicProperty = true;
	exposedObject.publicMethod = function() {
		// méthode exposée
	}
	return exposedObject;
})();

module.publicMethod();

Tout ce qui n’est pas ajouté à l’objet exposedObject reste invisible “à l’extérieur”, c’est ce qui différencie les méthodes privées des méthodes publiques du module.

Objets

Pour la déclaration d’objets, Florian conseille de s’en tenir à la notation littérale pour plus de clarté.

var obj = {
	prop : true,
	funct : function() {
		return 'me';
	}
};

Quand utiliser new ?

Que doit-on utiliser ?

var myvar1 = ApiObject();

ou

var myvar1 = new ApiObject();

La convention (tacite) : il faut utiliser new quand la fonction commence par une majuscule.
Si la méthode utilise this alors il faut utiliser new.
L’objet référencé par this dépend du contexte d’exécution.

Egalité

Lorsque l’on compare 2 objets avec ‘==’, ceux-ci sont éventuellement modifiés afin de pouvoir être comparés. L’intention est bonne mais est source de nombreux problèmes. Afin de lever toute ambiguïté on utilisera ‘===’ (triple égal) qui ne transformera pas les opérandes au préalable. Ainsi, si on compare 2 objets de types différents, on est alors certain que la triple égalité ne renverra jamais true.

typeof versus instanceof

Les 2 opérateurs servent le même but mais:
- typeof renvoie une chaine de caractères
- instanceof renvoie des informations plus complètes sur le type

On utilisera typeof avec les types simples du langage.
On utilisera instanceof avec les types customs

Les Type Wrappers

Ils n’apportent rien et gênent la lisibilité. On leur préférera une écriture claire et concise.

new Object() => {}
new Array() => []
new Number(42) => 42
new String('42') => '42'
new Boolean(true) => true
new Regexp('.*') => /.*/

Convertir un texte en nombre

parseInt peut donner des résultats surprenant en parsant une chaîne de caractères. En effet les lettres sont filtrées de manière transparentes lors de la conversion.
pour lever l’ambiguïté on peut préfixer la chaîne à parser par un +, on aura ainsi davantage de NaN (Not a Number) remontés.
Mon collègue Arthur Weber me signale également qu’on peut préfixer les variables à parser avec ‘~~’ ce qui en cas d’erreur renverra la valeur par défaut 0.

~~'foo'; // 0
parseInt('foo', 10); // NaN

Ce qui évite le type de code suivant

if (x === NaN ) x = 0;

Slides de la présentation : http://www.slideshare.net/FlorianBoulay/javascript-pour-lesdeveloppeursjava

22 Mar 21:32

Introduction à la programmation fonctionnelle

by Florence HERROU

On distingue deux grands paradigmes dans la programmation informatique :

  • L’impératif : basé sur une notion d’états modifiés par des instructions (le modèle de Turing), il s’agit de l’approche la plus répandue. Plus immédiate et intuitive, la plupart des langages informatiques utilisés (notamment les langages objet) sont basés sur ce paradigme.
  • Le déclaratif : le programme est décrit sous forme d’une description de la solution par rapport à un état initial. La programmation fonctionnelle en fait partie.

Facilement vue comme un joujou pour matheux aimant manier des concepts compliqués, la programmation fonctionnelle n’est pourtant pas incompatible avec les concepts de la programmation orienté objet. De plus, l’impératif n’a pas répondu à toutes les attentes : la concurrence, la parallélisation…
Scala, Haskell, Erlang ou Groovy sont des langages fonctionnels que vous avez pu rencontrer, mais il est possible également de coder différemment en java en utilisant quelques principes (et librairies) fonctionnels. Java 8 et la JSR 355 introduiront les bases du fonctionnel dans Java.
Voici les quatre concepts de la programmation fonctionnelle.

La pureté de la fonction

Une fonction pure, au sens mathématique, est une fonction qui ne change pas l’état du monde. Sa seule fonction est de prendre une ou des données en entrée et retourner un résultat en sortie. Quelle que soit le moment où la fonction est jouée, avec des données identiques en entrée, la sortie reste la même.
Il existe plusieurs intérêts à utiliser au maximum des fonctions pures :

  • Il n’y a aucun risque d’effet de bord à utiliser des fonctions, aucune variable cachée qui sera modifiée involontairement par cette fonction
  • Tester une fonction pure est très simple
  • La fonction peut être rejouée à l’infini : tant qu’elle recevra en paramètre la même donnée, elle retournera le même résultat, sans modifier quoi que ce soit
  • Il est également possible de ne pas jouer cette fonction, et gagner du temps si son résultat n’est pas utilisé

Nous prendrons comme exemple, afin d’illustrer chacun de ces principes, un programme permettant de calculer les occurrences suivantes de suites. Examinons la fonction ci-dessous.

fonction

On constate que cette fonction n’est pas pure. Un indice très clair est le fait qu’elle retourne “void”. Si cette fonction ne modifiait pas l’état du monde, elle ne servirait à rien. La modification se situe au niveau de la ligne : majFichier(suites);
Celle-ci met à jour les fichiers correspondant aux suites que l’on manipule. Nous voulons modifier ce fichier, il n’est donc pas question de rendre cette fonction pure. Cependant, pour tester bien plus facilement ce que nous faisons, il est possible d’extraire de cette fonction, un code qui peut être, lui intégré en fonction pure. C’est ce que nous faisons ci-dessous.

fonction-pure

La fonction calculeProchaineLigne est pure : tant que nous entrons la même chaîne de chiffres en paramètres, le retour sera identique. Elle peut donc être jouée à l’infini et être testée sans modifier l’état du monde.

L’immutabilité (ou immuabilité)

Il s’agit d’un concept allant de pair avec la pureté. Nous ne voulons pas changer l’état du monde, et ainsi nous ne changerons pas non plus l’état des paramètres passés à une fonction.
En java, nous utiliserons le mot-clef final.

Reprenons notre fonction précédente :

fonction-pure

La ligne : suite.setLigneCalculee(newLine); viole le principe d’immuabilité. D’une manière générale, l’utilisation de getter dans les classes est incompatible avec le principe d’immuabilité. Nous pouvons avoir à la place, dans notre classe Suite :

suite

De la même manière, notre liste passée en paramètre est actuellement modifiée en même temps qu’elle est parcourue. Nous allons donc créer une nouvelle liste dans laquelle nous insèrerons les nouvelles données, sans modifier la liste existante :

fonction-immutable

Ainsi, si la suite passée en paramètre est utilisée à un autre endroit du programme, aucun effet de bord n’est risqué. On gagne en simplicité de programmation.

L’expressivité

Cela correspond à l’utilisation de fonctions d’ordre supérieur, c’est-à-dire des fonctions qui prendront d’autres fonctions en paramètre.
Pour illustrer l’expressivité, nous utiliserons la bibliothèque Google Guava. Nous utiliserons l’interface Function pour définir comment obtenir un objet de la classe Suite avec la nouvelle ligne calculée à partir d’un objet de la classe Suite.

fonction-expressive-1

Nous pouvons l’utiliser grâce à la fonction transform de Google Guava qui applique une Function sur chaque élément de la liste passée en paramètre et retourne une nouvelle liste. Notre boucle est donc résumée en une seule ligne :

final List<Suite> newListe = Lists.transform(suites, function);

Composabilité

Il s’agit de la capacité de composer des fonctions ensemble pour obtenir une fonction.
Ainsi, si on modifie la suite de telle manière que la ligne suivante corresponde au calcul de la prochaine ligne sur laquelle on réapplique le même calcul, on aura tout simplement dans notre fonction :

String newLine = calculeProchaineLigne(calculeProchaineLigne(suite.getLastLine()));

La pureté et l’immutabilité nous permettent de faire cela sans prendre de risque.

Loin d’être un outil exotique, la programmation fonctionnelle peut nous permettre, en utilisant ses principes, de rendre nos codes plus lisibles et plus maintenables et ce, même dans des langages orientés objet.

22 Mar 20:04

Craftsman Recipes: Refactorez votre commit log avec Git

by Xavier Bucchiotty

Depuis quelques temps, les systèmes de gestion de sources distribués connaissent un regain d’intérêt fulgurant. Le plus connu est sans conteste Git avec une plateforme en ligne, Github.

Git est un outil qui repose sur un concept simple mais qui est redoutablement efficace.

Que peut offrir Git au développeur de plus qu’un système classique à la SVN ? Il n’y aura pas ici d’introduction au fonctionnement de Git mais plus une liste de bonnes pratiques. Car oui, le commit log est un livrable qui fait partie intégrante de l’application. En tant que craftsman, nous portons un intérêt à faire les choses proprement. L’historique des commits doit pouvoir être aussi propre que notre code. Nous allons voir comment Git nous permet de faire du refactoring de commit et de travailler de façon incrémentale et sûre.

Je ne reviendrai ici ni sur la structure de Git (différence entre stage, commit et push) ni sur les commandes de base :

  • git add
  • git commit
  • git checkout

Si ces concepts ne vous sont pas familiers, je vous conseille d’acheter ou de lire en ligne gratuitement ce guide complet.

Nous allons prendre ici pour exemple la construction d’un projet simpliste Java. Il n’y a pas besoin de repository distant. C’est là une des magie de Git, on peut tout tester en local ! Pour MacOS, je vous conseille GitX (L) comme outil graphique de visualisation de commit, Simple et efficace.

Tout au long de l’article, j’utiliserai la commande suivante pour lister les commits :

git log --oneline

git commit –amend

Tout d’abord, je crée un projet Java avec son pom.xml dans mon IDE préféré. Je crée ensuite le fichier .gitignore puis ajoute le pom.xml et ce fichier à Git pour enfin créer mon premier commit (69f904b). Je peux maintenant commencer à développer. Mais pour cela, il me faut au moins JUnit !

J’ajoute la dépendance au pom.xml et je crée deux classes fr.xebia.blog.User et sa classe de test. Je crée un commit uniquement avec la modification du pom.xml pour le séparer du commit de création de classes.

Si je regarde la branche master, j’ai la suite de commits :

31be36f Add the JUnit dependencies
69f904b Init

Pour tester le projet je lance mvn clean test. Je me rends alors compte que le répertoire target n’est pas ignoré. Je modifie le .gitignore dans ce sens. Seulement, je souhaite ajouter cette modification à mon commit de modification du pom.xml. Comment faire si ce commit est déjà effectué ? Avec Git, même si le commit à déjà été fait, je peux encore le changer !

J’ajoute la modification du .gitignore et je commit avec git commit –amendVoici maintenant que le commit log :

4f9194e Add the JUnit dependencies
69f904b Init

Comme attendu, je n’ai que deux commits. Seulement le SHA1 est différent ! Pourquoi ? Parce qu’un commit est créé une seule fois et n’est pas modifiable. En faisant un amend, Git a créé un tout nouveau commit qui contient les modifications du premier commit en plus de celles que vous venez d’ajouter. Tout cela est bien sûr transparent.
Il est facile de se rendre compte de cela en ajoutant des tags sur les commits avant et après l’opération.

Je peux maintenant faire le commit de mes classes. Au final, j’ai le commit log suivant :

7c0d84a Create user
4f9194e Add the JUnit dependencies
69f904b Init

rebase interactif

Je viens de créer mes classes mais je me rends compte que j’ai oublié de mettre JUnit en scope test dans mon POM. J’aimerais bien que cette modification soit dans le commit d’ajout de JUnit. Comment faire pour modifier un commit qui n’est pas le dernier créé ? La solution : git rebase -i
Il permet tout simplement de réécrire l’histoire. Démonstration :

Tout d’abord, je commites la modification du scope dans le pom.xml. J’ai alors les logs suivants :

6f474a0 Add Junit in scope test
7c0d84a Create user
4f9194e Add the JUnit dependencies
69f904b Init

Nous allons ensuite fusionner les commits 6f474a0 et 4f9194e. Pour cela, je peux réécrire l’histoire de mes trois derniers commits :
git rebase -i HEAD~3

Git affiche ensuite le commit log courant avec des opérations possibles, je choisi de déplacer les commits de place pour placer l’ajout du scope sous l’ajout de la dépendance. Comme opération sur ce commit, je choisi fixup.

pick 4f9194e Add the JUnit dependencies
fixup 6f474a0 Add Junit in scope test
pick 7c0d84a Create user

J’enregistre mes modifications et voici le résultat:

e3f366e Create user
e888982 Add the JUnit dependencies
69f904b Init

Je n’ai toujours que trois commits. Le commit 4f9194e est devenu e888982 et 7c0d84ae3f366e. Le premier commit a changé car nous avons ajouté son contenu. Par contre, pourquoi le dernier commit a été modifié ?
Un commit est composé entre autre :

  • une liste de modification ;
  • à partir d’un ensemble de commits parents (il peut y en avoir plusieurs dans le cas d’un merge).

Si j’inspecte ce commit de création de classe avant l’opération de rebase, voici le détail des informations dans Gitx

Son parent est le commit 4f9194e.

Voici le détail du nouveau commit :

Son parent est le commit e888982, c’est à dire le nouveau commit contenant les deux modifications fusionnées.

Si on regarde l’ensemble du commit log, voici ce que l’on obtient :

On retrouve toutes les branches qui ont été créées automatiquement par Git. Mais la branche master est quant à elle propre à ce qui est le résultat attendu.

rebase interfactif + merge

J’ai ajouté deux commits sur master dont voici la log:

fcda951 THAT'S A CHANGE
a85c4e9 USELESS CHANGES
e3f366e Create user
e888982 Add the JUnit dependencies
69f904b Init

On est jeudi soir, et je dois créer une version de l’application en urgence ! Seulement sur master, il y a un commit de trop (a85c4e9 USELESS CHANGES). Comment faire ? Je peux faire comme sous SVN : un revert du commit, faire ma version, puis un revert de revert du commit, ce qui est bien mais l’on peut mieux faire. Comme Git me permet de créer des branches facilement, je vais me servir de cette fonctionnalité !

Je créé une branche bugfix au niveau de master avec la commande :
git checkout -b bugfix

Ensuite, j’utilise le rebase interactif pour supprimer le commit choisi.
git rebase -i HEAD~3

Je supprime le commit de ma liste de commandes :

pick 1cbf6bc THAT'S A CHANGE
pick e3f366e Create user

Voici le commit-log suite à cette opération :

Je réalise ensuite ma version, c’est à dire :

  • un commit de version 1.0 ;
  • un tag 1.0 ;
  • un commit de passage à la version suivante.

La version est créée. Je peux maintenant revenir sur master et monter la version sur cette branche en faisant un merge de la branche bugfix sur master pour ensuite supprimer cette branche temporaire.

git checkout master
git merge bugfix
git branch -d bugfix

Le commit log est alors le suivant :

L’avantage de cette méthode est de permettre de modifier l’historique de commit qui ont déjà été poussés sur un repository distant. En effet, c’est sur une branche que nous avons écrit une version parallèle de l’histoire. Je n’ai pas touché à l’historique de master. Cela est au prix d’un commit de merge qui ne sont parfois pas simple à lire. Mais il n’y a pas de revert de revert de revert…

git reset HEAD^

Je viens de faire un commit (bb0b66b WRONG AMEND). Seulement, j’ai mis à l’intérieur deux fichiers qui n’ont rien à voir. Je souhaiterais plutôt avoir deux commits séparés. C’est le rôle de la commande suivante :
git reset HEAD^

Voici le résultat de cette commande dans la console :

Unstaged changes after reset:
M src/main/java/fr/xebia/blog/Group.java
M src/main/java/fr/xebia/blog/User.java

Je peux ensuite créer maintenant mes deux commits :

git add src/main/java/fr/xebia/blog/Group.java
git commit -m "yet another useless commit"

git add src/main/java/fr/xebia/blog/User.java
git commit -m "another log change"

A la fin, j’ai le commit log attendu :

C’est une commande très utile après un git commit -am ou un git add . un peu abusif.

git checkout

La version de l’application courante est défectueuse et l’on vous demande de tester un algorithme dans une version précédente. C’est le rôle du git checkout. Contrairement aux autres commandes vues, celle-ci ne réécrit pas l’histoire mais permet de s’y promener. Cette commande déplace la tête de lecture (HEAD) sans modifier aucune référence sur les branches.
Je peux par exemple sur mon système de fichier revenir à tout moment au tout premier commit de mon application :

git checkout 69f904b

Voici le message de Git :

Note: checking out '69f904b'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

git checkout -b new_branch_name

HEAD is now at 69f904b... Init

Vous êtes revenus sur un commit passé qui a déjà un futur. Et il n’est pas possible de réécrire le futur. Alors Git vous prévient et vous propose de créer une branche pour pouvoir en écrire un futur parallèle. Si vous travaillez sur ce commit sans créer de branche, ces commits seront enregistrés mais seront éligibles à n’importe quel moment au garbage collecting de git ! Ils ne seront référencés que par leur SHA1 (identifiant unique).

git reset –hard

Du point de vue de mon système de fichier, cette commande est équivalente à la précédente. Votre système de fichier reflète l’état du projet au commit demandé. Seulement, Git va en plus changer la référence de la branche courante. Voici dans Gitx le détail du dernier commit de la branche master:


On voit que ce commit est référencé par master. Une branche dans Git n’est qu’une référence vers un commit. Avec git reset –hard, je modifie la référence de la branche en cours. Je peux donc reprendre tout mon travail depuis le début avec la commande suivante :

git reset --hard 69f904b

Voici le résultat dans gitx :

C’est maintenant le commit 69f904b qui est référencé par master et mon commit log est vide !

Si vous n’avez pas tagué le commit de master avant, et bien vous l’avez tout simplement perdu. Cette commande est donc pour mois l’une des plus sensibles de Git. A manier avec prudence !

git reflog

Bon, en fait vous n’êtes pas forcément totalement perdu. Il existe encore une porte de sortie dans Git, git reflogLe reflog contient la liste de tous les mouvements que vous avez fait dans l’historique. Il est donc totalement personnel et n’est pas poussé sur un repository distant. Utilisé avec git reset –hard, il permet de revenir dans un état connu à tout moment. Pour me sauver de la dernière situation, voici ce que me dit la commande git reflog :

69f904b HEAD@{0}: 69f904b3a3226fd36722dedd88d57539163db3ff: updating HEAD
00fccf7 HEAD@{1}: checkout: moving from bb0b66b3b9b4b0e4b238d76700d9dc6b39c19615 to master

HEAD@{0} est la position courante. Effectivement, je suis sur 69f904b. Mais juste avant master était positionné sur 00fccf7. Il me suffit donc de refaire la commande suivante :

git reset --hard 00fccf7

Et j’ai retrouvé la situation initiale.

Le guide du voyageur intergalactique

Comme sur la quatrième de couverture de ce fameux guide, si vous avez un problème avec Git, surtout "DON’T PANIC".

 

Les forums sont assez fournis en trucs et astuces. Vous retrouverez par exemple ici une "cheat sheet" bien pratique.

 

Conclusion

Nous l’avons vu, Git est un outil puissant. Entre autre, il permet de retravailler finement ses commits pour un livrable propre. Mais cette puissance a un prix. On peut vite faire des dégâts si l’on ne maitrise pas ce que l’on fait. Si vous souhaitez travailler à nouveau certains commits, voici quelques règles d’or :

  • Ne touchez qu’à des commits qui ne sont pas encore sur un repository distant (postérieur aux tags origin/XXXX) ;
  • Utilisez le git reflog et reset –hard seulement en cas d’urgence.

Pour bénéficier de toutes ces possibilités, je vous conseille fortement d’activer le mode rebase lors des pull avec la configuration suivante :

git config branch.autosetuprebase always

Cela n’est valable que sur des branches pour lesquelles les durées de vie sont courtes (quelques jours à peine). Sinon, cela risque d’être la chasse au conflit à chaque rebase. 

A vous de choisir le mode de merge le plus adapté à votre situation. Mais vaut-il mieux perdre cinq minutes à retravailler ses commits avant de pousser ou alors laisser son prochain se débrouiller en maintenance avec des logs inutilisables ?

 

22 Mar 20:03

Le gestionnaire de cache de Guava

by Charles Blonde

L’objectif de cet article est de présenter le gestionnaire de cache disponible dans la bibliothèque Guava.
Guava est une bibliothèque utilitaire publiée par Google qui couvre un grand nombre de domaines (collections, IO, reflections, programmation fonctionnelle, etc …) et est souvent déjà présente dans un grand nombre de nos projets.
Cependant, la raison de l’inclusion de cette bibliothèque est rarement l’utilisation du gestionnaire de cache alors qu’il a toute sa place dans de nombreuses situations.

Rappels sur les gestionnaires de caches

Utiliser un gestionnaire de cache est une problématique qui revient très souvent lors de nos développements et nous avons alors à disposition de nombreuses solutions disponibles :

  • L’interface Map lorsque le besoin est très simple :
    • La quantité d’objets à stocker est plus ou moins connue à l’avance et ne risque pas d’impacter la mémoire de la JVM ;
    • Une fois calculés, ces objets sont valables pendant toute la durée de vie de l’exécution de l’application. Il n’y a donc jamais de raison de les calculer à nouveau.
  • L’interface ConcurrentMap lorsque les besoins sont similaires à Map mais avec un accès multi-threads
  • Ehcache: le plus populaire des frameworks de cache dans l’écosystème Java, avec entre autre comme fonctionnalités :
    • Gestion des TTL
    • Gestion d’une taille max avec l’algorithme LRU (Least Recently Used)
    • Persistance des données sur disque
    • Synchronisation des caches entre plusieurs JVM
  • Memcached/Redis : cette fois-ci il s’agit d’éléments d’infrastructure qui permettent à plusieurs applications/JVM d’y accéder de manière centralisée

Le gestionnaire de cache de Guava vient s’intercaler entre l’utilisation d’une ConcurrentMap et Ehcache en ne reprenant par défaut que les fonctionnalités basiques de ce dernier :

  • Gestion des TTL
  • Gestion des évictions (ex: nombre max d’éléments)

Dans de nombreux cas, ce sont les seules caractéristiques qui nous intéressent et il s’agit alors du domaine de prédilection de l’utilisation de Guava.

Utilisation de Guava

Utilisation classique

Il est possible d’utiliser le gestionnaire de cache de Guava avec un algorithme classique comme nous le ferions avec Ehcache ou Memcached :       

//Creation d'un cache classique avec des String comme clefs et valeurs
Cache<String, String> cache = CacheBuilder.newBuilder()
    .maximumSize(100) // Taille Max
    .expireAfterWrite(1, TimeUnit.MINUTES) // TTL
    .build();

String myValue = cache.getIfPresent("myKey_1");
if (myValue == null) {
    //Calcul de la valeur et mise en cache
    myValue = computeValue("myKey_1");
    cache.put("myKey_1", myValue);
}

Cependant, il est préférable de l’utiliser de manière beaucoup plus élégante en utilisant un CacheLoader ou un Callable.

Utilisation d’un CacheLoader

Dans cette situation, nous allons indiquer au gestionnaire de cache comment calculer les valeurs directement lors de l’initialisation du cache.
Ainsi, tout appel au cache entraînera un calcul de la valeur si celle ci n’est pas déjà présente.

LoadingCache<String,String> cache = CacheBuilder.newBuilder()
    .maximumSize(100)
    .expireAfterWrite(1, TimeUnit.MINUTES)
    .build(new CacheLoader<String, String>() {
        @Override
        public String load(String s){
            // Warning : must never return null  !
            return computeValue(s);
        }
    });

//Never null
String myValue = cache.get("myKey_1");

A noter que deux appels en simultanés de la recherche de la même clef ne provoquera pas un deuxième calcul de la valeur comme cela peut être le cas avec la méthode classique : le second appel sera mis en attente du résultat du premier appel.
Dans cet exemple, j’utilise la méthode de l’interface LoadingCache

V get(K key) throws ExecutionException;

mais il existe aussi la méthode

V getUnchecked(K key);

qui cette fois ne provoque pas la levée d’exception et donc ne vous oblige pas à faire de blocs try/catch. À utiliser donc, suivant vos cas d’utilisations.

Cette méthode est bien adaptée lorsque les données du cache sont homogènes et du même type. Par exemple, lorsqu’il s’agit de cacher des objets en provenance d’une base de données.
Cependant, il arrive que nous utilisions un cache pour y stocker des données hétérogènes comme par exemple des résultats de web service avec des appels différents suivant la clef. Dans ce cas, l’utilisation d’un Callable est adaptée.

Utilisation d’un Callable

Imaginons le cas d’utilisation suivant :
Vous avez une application de type E-Commerce qui en plus de vendre sur votre site va publier vos objets sur EBay. Afin de créer une enchère, vous avez dans un premier temps besoin de récupérer certains types de base : les durées de mise en vente disponibles pour une catégorie, les conditions des objets (neuf, occasion, HS, etc ..), les types de transports disponibles, etc.

Bien entendu, vous n’allez pas aller chercher la liste de ces valeurs disponibles à chaque fois que vous voulez créer une enchère, vous allez garder ces données en cache.
Cependant, les web services à appeler sont différents et il n’est donc pas possible d’utiliser un CacheLoader générique car les données sont hétérogènes.
Pour résoudre ce problème, le gestionnaire de cache de Guava met à notre disposition le chargement des données par Callable :  

public class EbayCacheService {
    //Service appelant les web services EBay
    EbayService ebayService = new EbayService();

    //Notre cache
    Cache<String, List> cache = CacheBuilder.newBuilder()
            .maximumSize(20)
            .expireAfterWrite(1, TimeUnit.DAYS) // Expire après une journée
            .build();

    public List getConditionValues(final String categoryId) throws ExecutionException {
        return cache.get("conditions:" + categoryId, new Callable<List>() {
            public List call() {
                return ebayService.getConditionValues(categoryId);
            }
        });
    }

    public List getListingDurations(final String categoryId) throws ExecutionException {
        return cache.get("durations:" + categoryId, new Callable<List>() {
            public List call() {
                return ebayService.getListingDurations(categoryId);
            }
        });
    }

    public List getShippingDetailsTypes() throws ExecutionException {
        return cache.get("shippingDetailsTypes", new Callable<List>() {
            public List call() {
                return ebayService.getShippingDetailsTypes();
            }
        });
    }
}

Pour chaque type d’objet, nos méthodes de chargement vont donc être différentes.

Évictions

L’éviction est la principale caractéristique qui différencie l’utilisation d’un cache basé sur une Map et l’utilisation d’un gestionnaire de cache plus évolué. Elle permet de garantir que l’utilisation mémoire et la fraîcheur des données seront toujours sous contrôle.

Time to live

Lors des exemples précédant, nous avons utilisé une éviction basée sur la date d’insertion des données dans le cache avec la méthode

public CacheBuilder<K, V> expireAfterWrite(long duration, TimeUnit unit) {}

Il est aussi possible de se baser sur la date du dernier accès (écriture et/ou lecture) :

public CacheBuilder<K, V> expireAfterAccess(long duration, TimeUnit unit){}

Poids des objets

Au lieu de se baser sur le nombre d’éléments dans le cache, il est aussi possible de se baser sur le poids des objets. Ceci est particulièrement pertinent dans le cas d’utilisation d’objet hétérogènes où des objets très légers peuvent côtoyer d’importantes collections.
Dans cette situation, il est difficile de déterminer le nombre maximum d’éléments à garder en cache sans risquer une utilisation mémoire incontrôlée.

Contrairement à ce que l’on pourrait penser, cette utilisation ne se base pas sur la taille en octets (difficile à calculer en Java) mais à un poids déterminé par l’utilisateur. En reprenant l’exemple utilisé pour les Callable, il est possible de se baser par exemple sur le nombre d’objets présent dans chaque liste :

Cache<String, List> cache = CacheBuilder.newBuilder()
            .maximumSize(20)
            .expireAfterWrite(1, TimeUnit.DAYS)
            .maximumWeight(1000) // Nombre maximum d’objet au total contenu dans les listes
            .weigher(new Weigher<String, List>() {
                @Override
                public int weigh(String key, List value) {
                    return value.size();
                }
            })
            .build();

Weaks et Softs references

Même si ce n’est pas conseillé, il est possible de faire appel à des références weaks pour les clefs et les valeurs ce qui transfert la gestion de l’éviction au Garbage Collector.

Des softs références peuvent aussi être utilisées pour les valeurs. Bien que non testé, en théorie, il doit être possible de créer un cache qui va utiliser près de 100% de la heap et libérer la mémoire seulement lorsque le Garbage Collector en aura besoin pour allouer de nouveaux objets.

Un très bon article pour comprendre les subtilités des weaks et softs références est disponible sur java.net : http://weblogs.java.net/blog/2006/05/04/understanding-weak-references

Fonctionnalités avancées

Le gestionnaire de cache de Guava possède des fonctionnalités avancées mais celles-ci sont désactivées par défaut. La philosophie derrière Guava étant de laisser ces choix entre les mains des développeurs.

Nettoyage des données

Contrairement à Ehcache, Guava n’utilise pas un thread séparé pour supprimer les données expirées du cache.
En effet, le gestionnaire de cache de Guava va effectuer sa maintenance lors de chaque écriture ou lecture si les écritures sont trop rares.
Par conséquent, les objets arrivés à expiration seront quand même conservés si le cache n’est pas/plus utilisé.
Si besoin, il est possible de gérer manuellement ce nettoyage en appelant la méthode Cache.cleanUp() voire de créer un thread qui va appeler cette méthode à intervalle régulier.

Statistiques

Il est possible d’activer les statistiques lors de la création d’un cache avec la méthode Cachebuilder.recordStats().
Il sera alors possible d’utiliser la méthode Cache.stats() pour accéder à un nombre important de métriques comme le taux de hits et le nombre d’évictions et ainsi pouvoir publier ces informations en JMX très facilement.

Remove Listener

Afin de monitorer les suppressions d’éléments du cache, il est possible de rajouter un RemoveListener lors de la création du cache et ainsi de garder une trace de toutes les évictions.

Cache<String, List> cache = CacheBuilder.newBuilder()
    .removalListener(new RemovalListener<String, List>() {
        @Override
        public void onRemoval(RemovalNotification<String, List> removal) {
            //removal.getValue();
        }
    })
    .build();

Guava en tant que Cache de second niveau

Bien que totalement autonome, il est facilement possible d’utiliser Guava en tant que cache de second niveau qui va chercher ses informations dans un premier temps dans un gestionnaire de cache de type infrastructure. L’utilisation d’un CacheLoader est tout à fait adapté à cette situation. Voici un exemple utilisant Redis et Guava en tant que cache de second niveau :

CacheLoader<String, String> redisCacheLoader = new CacheLoader<String, String>() {
    @Override
    public String load(String key) throws Exception {
        //Check value in Redis
        Jedis jedis = new Jedis("redis-hostname", 6379);
        String value = jedis.get(key);

        //If not present, compute and put it in Redis
        if (value == null) {
            value = computeValue(key);
            jedis.set(key, value);
        }
        return value;
    }
    };

Cache<String, String> cache = CacheBuilder.newBuilder()
    .maximumSize(100)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .build(redisCacheLoader);

Le fonctionnement est donc le suivant :

  • Dans un premier temps, les valeurs seront issues de Guava ;
  • Si la clef recherchée n’est pas présente dans le cache Guava, une recherche aura lieu dans Redis ;
  • Si la clef n’est pas trouvée dans Redis, la valeur associée sera calculée et ajoutée à Redis.

Grâce à ce mécanisme, nous bénéficions aussi de la recherche unique proposée par Guava : si deux appels ont lieu avec la même clef en parallèles, le calcul ne sera fait qu’une seule fois et le second thread sera mis en attente du premier.

Refresh

Guava propose le refresh du cache par le biais des clés déjà présentes. La force de ce cache réside dans le fait qu’il s’effectuera de manière asynchrone. Pour ce faire, nous devons surcharger le méthode CacheLoader.reload(K, V) :

final ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(2));


LoadingCache<User, List<Product>> cache = CacheBuilder
 .newBuilder()
 .refreshAfterWrite(2, TimeUnit.MINUTES)
 .build(new CacheLoader<User, List<Product>>() {
  @Override
  public List<Product> load(User user) throws Exception {
   return loadUserFromDatabase(user);
  }
  @Override
  public ListenableFuture<List<Product>> reload(final User user, List<Product> oldValue) throws Exception {
   return listeningExecutorService.submit(new Callable<List<Product>>() {
    public List<Product> call() throws Exception {
     return load(user);
    }
   });
  }
 });

Comment et quand est appelée cette méthode ?

Lors de la déclaration de notre cache, nous avons défini la propriété refreshAfterWrite. Cette méthode permet de rendre une clé éligible au refresh après la période spécifiée en paramètre (2 minutes dans notre exemple).  Attention, cela signifie que la valeur de la clé sera rafraîchie lors du prochain accès au cache, qui peut être bien au delà des deux minutes. Dans cette situation, le premier appel d’une clé déjà en cache ayant lieu après 2 minutes retournera l’ancienne valeur et la nouvelle sera calculée pour un appel ultérieur.

Combiné avec la méthode expireAfterWrite, il est possible d’avoir un cache « intelligent » :

CacheBuilder.newBuilder().refreshAfterWrite(2,TimeUnit.MINUTES).expireAfterAccess(4, TimeUnit.MINUTES).build();

Dans cet exemple, une clé peut devenir éligible à un refresh au bout de 2 minutes et si aucun accès au cache n’est effectué au bout des 4 minutes, cette clé sera déclarée obsolète.

Asynchrone

Nous voyons que la méthode reload renvoie un ListenableFuture. Cet objet est similaire à java.concurrent.Future mais permet en plus d’appeler une callback qui est exécutée dans un ExecutorService. Ce dernier initialise un ThreadPool permettant ainsi des accès à notre cache non bloquants.

Conclusion

Bien que très léger, Guava reprend les principales caractéristiques que l’on est en droit d’attendre d’un gestionnaire de cache moderne. Certaines ne sont pas activées par défaut et le choix reste entre les mains des développeurs.
Si vos projets utilisent déjà Guava alors vous n’avez aucune excuse pour ne pas utiliser son mécanisme de cache lorsque le besoin s’en fera sentir.
Si ce n’est pas le cas, il s’agit peut être de l’opportunité parfaite pour l’inclure et par la suite bénéficier des très nombreux outils disponibles dans cette bibliothèque.
Cependant, il s’agit d’un gestionnaire de cache “In Process” qui n’est pas forcément adapté à toutes les situations : taille importante, accès à partir de plusieurs JVM, sharding, persistance.
Comme souvent dans le monde Java, la complexité revient à choisir le bon outil dans la bonne situation.

Cette article est grandement inspiré de la documentation officielle : http://code.google.com/p/guava-libraries/wiki/CachesExplained

22 Mar 20:01

Introduction aux tests unitaires en javascript

by Florent Duveau

De la touche "F5" aux frameworks de tests

Les tests unitaires sont aujourd’hui une norme dans le développement des applications Java. L’amplification des techniques Agiles et du mouvement Software Craftsmanship ont poussé à mettre les tests unitaires comme prérequis au développement d’applications.
 
Concernant le développement d’applications front en javascript, les tests se limitent souvent à une vérification manuelle du comportement attendu sur le navigateur. La touche “F5” et les “alert” en sont les principaux outils. Qui n’a pas passé des heures à rafraîchir sa page et à regarder les messages d’alertes pour comprendre le changement de comportement d’un module lors d’une modification du code ?
 
Ceci entraîne une perte de temps énorme, la confiance envers son code lorgnant vers 0 ainsi qu’une maintenance qui devient quasiment impossible.
 
Heureusement, au fil du temps les navigateurs ont sorti des outils permettant de faciliter les tests du code tels que Firebug pour Firefox ou le puissant outil de développement présent dans Chrome (je n’ai malheureusement pas encore utilisé les outils IE).  Ces outils sont d’une aide inestimable mais les tests sont toujours fait manuellement.
Des frameworks de tests unitaires pour Javascript existent pourtant depuis plus de 10 ans. L’apparition de Node.js et de frameworks de développement front en javascript (Angular, Backbone, Ember…) ont fait passer le javascript dans une dimension supérieure et permettent aujourd’hui le développement partiel ou total d’applications dans ce langage. Ce développement impose de facto l’utilisation de tests (unitaires, d’intégration…).
 
Nous allons voir dans cet article la logique des tests unitaires en javascript pur afin de mieux appréhender le fonctionnement des frameworks de tests automatisés tel que Jasmine, mocha ou QUnit. Nous montrerons d’ailleurs une version de ces tests avec QUnit afin de commencer à voir les possibilités que peuvent nous offrir ces frameworks.

Mon premier test unitaire

 
Prenons une fonction javascript simple qui convertit un montant donné en euros en un montant en dollars et appelons-la, disons, euroToDollar. À l’heure où ces lignes sont écrites, 1 € vaut (environ) 1,30 $. J’aimerais donc vérifier que quand on donne 1 à la fonction, celle-ci me renvoie 1,30.

function euroToDollar(euro){
 return 1.3;
}

Dans une version très simple, je pourrais faire : console.log(euroToDollar(1)) et vérifier que j’ai bien 1,30.
 
Pour ce faire j’écris un fichier de tests et intégrons le dans une page html, comme ceci :

console.log(euroToDollar(1));
console.log(euroToDollar(2));
<!doctype html>
<html>
 <head>
  <meta charset="utf-8">
  <title>euroToDollar tests</title>
 </head>
 <body>
  <script type="text/javascript" src="euroToDollar.js"></script>
  <script type="text/javascript" src="euroToDollar-test.js"></script>
 </body>
</html>

J’ai maintenant une petite série de tests qui me permet de voir que 1€ retourne bien 1,30$ mais que 2€ ne nous retourne pas 2,60$.
 
Je modifie ma fonction en conséquence :

function euroToDollar(euro){
 return euro * 1.3;
}

Voici notre premier test unitaire. Vous avez sûrement déjà fait cela un grand nombre de fois pour vérifier le comportement de vos fonctions.
Nous devons cependant toujours contrôler manuellement nos données. Pour automatiser la vérification, nous pouvons nous reposer sur le principe des assertions.
 
L’assertion est le coeur du test unitaire. Il permet au système de déterminer si un test s’est déroulé avec succès ou non.
 
J’écris donc une fonction d’assertion simple et l’utilise pour nos tests :

function assert(message, expr){
 if(!expr){
  throw new Error(message);
 }
}

Je voudrais aussi pouvoir afficher un message en vert si le test s’est bien passé, en rouge sinon.

function assert(message, expr){
 if(!expr){
  output(false, message);
  throw new Error(message);
 }
 output(true, message);
}

function output(result, message){
 var p = document.createElement('p');
 message += result ? ' : SUCCESS' : ' : FAILURE';
 p.style.color = result ? '#0c0' : '#c00';
 p.innerHTML = message;
 document.body.appendChild(p);
}



assert('1€ should return 1,3$', euroToDollar(1) === 1.3);
assert('2€ should return 2,6$', euroToDollar(2) === 2.6);

Et voilà ! Je peux maintenant tester ma fonction dans une page web et afficher un message en vert si le test a réussi ou en rouge sinon.
J’ai maintenant une fonction d’assertion et une page de tests unitaires facilement lisible pour ma fonction de conversion.
 
Développons un peu cette fonction de conversion et faisons en sorte qu’elle puisse convertir notre euro en plusieurs monnaies différentes en ajoutant un paramètre de devise en entrée :
( euroToDollar.js devient convertEuro.js et euroToDollar-test.js devient convertEuro-test.js

function convertEuro(euro, currency){
 switch(currency){
  case 'USD' :
   return euro * 1.3; 
  case 'GBP' :
   return euro * 0.87;
  case 'JPY' :
   return euro * 124.77;
  default : {
   throw new Error('Currency not handled');
  }
 }
}

J’ai maintenant plus de cas à gérer dans mes tests et j’aimerais pouvoir séparer nos tests en fonction de la devise testée. C’est ici que le concept de “suite de tests” entre en jeu. Il me permet de créer une suite de tests reposant sur une logique du code.
 
J’ajoute la fonction de suite de tests :

function testcase(message, tests){
 var total = 0;
 var succeed = 0;
 var p = document.createElement('p');
 p.innerHTML = message;
 document.body.appendChild(p);
 for(test in tests){
  total++;
  try{
   tests[test]();
   succeed++;
  }catch(err){  
  }
 }
 var p = document.createElement('p');
 p.innerHTML = 'succeed tests ' + succeed + '/' + total ;
 document.body.appendChild(p);
}

Et voici mes suites de tests pour nos différentes devises :

testcase('I convert euro to usd', {
 'I test with one euro' : function(){
  assert('1€ should return 1,3$', convertEuro(1, 'USD') === 1.3);
 },
 'I test with two euros' : function(){
  assert('2€ should return 2,6$', convertEuro(2, 'USD') === 2.6);  
 }
})

testcase('i convert euro to gbp', {
 'I test with one euro' : function(){
  assert('1€ should return 0,87£', convertEuro(1, 'GBP') === 0.87);
 },
 'I test with two euros' : function(){
  assert('2€ should return 1,74£', convertEuro(2, 'GBP') === 1.74);
 }
})

testcase('i convert euro to jpy', {
 'I test with one euro' : function(){
  assert('1€ should return 124,77¥', convertEuro(1, 'JPY') === 124.77);
 },
 'I test with two euros' : function(){
  assert('2€ should return 249,56¥', convertEuro(2, 'JPY') === 249.56);
 }
})

testcase('I try with currency not handled by the function', {
 'I try with NZD' : function() {
  var messsage;
  try{
   convertEuro(1, 'NZD')
  } catch(err){
   message = err;
  }
  assert('convert euro to nzd should throw error', message = 'Currency not handled');
 }
})

Mes tests sont donc bien séparés par unité logique.
J’affiche pour chaque suite de tests le nombre de tests réussis sur le total de tests passés.


 
Une dernière chose, j’aimerai pouvoir définir la devise au début de chaque suite de tests et l’utiliser comme variable afin de pouvoir la changer facilement et éviter des erreurs de saisie.
Je fais appel ici au concept de setup et de teardown.

function testcase(message, tests){
 var total = 0;
 var succeed = 0;
 var hasSetup = typeof tests.setUp === 'function';
 var hasTeardown = typeof tests.tearDown === 'function';
 var p = document.createElement('p');
 p.innerHTML = message;
 document.body.appendChild(p);
 for(test in tests){
  if(test !== 'setUp' && test !== 'tearDown'){
   total++;
  }
  try{
   if(hasSetup){
    tests.setUp();
   }
   tests[test]();
   if(test !== 'setUp' && test !== 'tearDown'){
    succeed++;
   }
   if(hasTeardown){
    tests.tearDown();
   }
  }catch(err){  
  }
 }
 var p = document.createElement('p');
 p.innerHTML = 'succeed tests ' + succeed + '/' + total ;
 document.body.appendChild(p);
}

testcase('I convert euro to usd', {
 'setUp' : function(){
  this.currency = 'USD';
 },
 'I test with one euro' : function(){
  assert('1€ should return 1,3$', convertEuro(1, this.currency) == 1.3);
 },
 'I test with two euros' : function(){
  assert('2€ should return 2,6$', convertEuro(2, this.currency) == 2.6);  
 }
})
...

La fonction setUp sera appelée avant chaque test et la fonction tearDown après chaque test.
 
J’ai maintenant une suite de tests unitaires automatisés qui me permet de valider réellement le bon fonctionnement de ma fonction de conversion de devises. Ces tests me permettront de vérifier la non régression de ma fonction lors de refactoring ou d’ajout de nouvelles fonctionnalités et de tester celles-ci facilement sur différentes plateformes (firefox, chrome, IE…).

Et avec un framework ?

Nous venons de voir une solution "maison" de la mise en place de tests unitaires. Cette solution suffit à des projets simples mais peut vite montrer ses limites sur des projets de plus grande envergure. Heureusement, des frameworks de tests javascrit sont là pour nous donner les outils nécessaires à nos besoins.
Je vais maintenant tester ma fonction convertToEuro avec le framework QUnit, en écrivant les mêmes tests que ma solution maison.

Après avoir téléchargé les sources sur le site, je crée la page html de présentation des résultats :

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>convertToEuro tests with QUnit</title>
    <link rel="stylesheet" href="qunit.css">
</head>
<body>
 <div id="qunit"></div>
 <div id="qunit-fixture"></div>
 <script src="qunit.js"></script>
 <script src="../convertEuro.js"></script>
 <script src="converEuro-test-QUnit.js"></script>
</body>
</html>

Voici la page des tests :

module('I convert euro to usd',{
    setup: function() {
        this.currency = 'USD';
    }
});
test('1€ should return 1,3', function() {
    strictEqual(convertEuro(1, this.currency), 1.3, 'succeed !');
});
test('1€ should return 2,6', function() {
    strictEqual(convertEuro(2, this.currency), 2.6, 'succeed !');
});

module('I convert euro to gbp',{
    setup: function() {
        this.currency = 'GBP';
    }
});
test('1€ should return 0,87£', function() {
    strictEqual(convertEuro(1, this.currency), 0.87, 'succeed !');
});
test('2€ should return 1,74£', function() {
    strictEqual(convertEuro(2, this.currency), 1.74, 'succeed !');
});

module('I convert euro to jpy',{
    setup: function() {
        this.currency = 'JPY';
    }
});
test('1€ should return 124,77¥', function() {
    strictEqual(convertEuro(1, this.currency), 124.77, 'succeed !');
});
test('2€ should return 249,56¥', function() {
    strictEqual(convertEuro(2, this.currency), 249.56, 'succeed !');
});

module('I try with currency not handled by the function');
test('I try with NZD', function(){
    throws(
        function() {
            convertEuro(1, 'NZD');
        },
        /Currency not handled/,
        "throws error Currency not handled"
    );
});

Et voici le résultat :

On peut remarquer que la logique des tests est pratiquement la même. Nos testcase sont appelés ici des module et nos assert maisons sont remplacés par l’assertor de QUnit strictEqual.
QUnit a aussi les notions de setup et teardown qui sont définis dans chaque module de tests.

Tout en gardant la même logique, QUnit introduit de nouveaux outils tel que la vérification des erreurs grâce à son throws utilisé ici pour valider que notre fonction ne prend pas en compte la devise NZD.

QUnit fournit aussi des outils de callbacks, de configuration de l’environnement de tests ainsi que la possibilité de tester des fonctions asynchrones. Il existe aussi de nombreux plugins permettant de rajouter des logs, de mocker des appels ajax etc.

QUnit fournit aussi une interface d’affichage de résultats détaillés. On peut voir la version du navigateur utilisée, le nombre de tests lancés, réussis et échoués. Une chose très appréciable est que QUnit nous donne des informations sur les raisons d’échec d’un test. Concernant le test 6, on peut rapidement voir quel était le résultat attendu et celui retourné. Efficace.

Vous pouvez retrouver les sources de cet article sur ce github.

Et ensuite ?

Nous voyons qu’il est très facile d’implémenter des tests unitaires pour nos fonctions en javascript. L’exemple montré dans cet article est très basique et les besoins sont souvent plus complexes. Les frameworks javascript de tests reposent la plupart du temps sur la logique présentée dans cet article et mettent à disposition de nombreux outils tels que des spies, des stubs ou des mocks pour répondre aux besoins d’applications plus complexes. Nous découvrirons tous ces outils lors d’un prochain article et nous verrons toute la puissance que peut nous offrir ces frameworks pour garantir le bon fonctionnement de nos applications javascript !

22 Mar 19:48

Le gestionnaire de cache de Guava

by Olivier Pitton
L'objectif de cet article est de présenter le gestionnaire de cache disponible dans la bibliothèque Guava. Guava est une bibliothèque utilitaire publiée par Google qui couvre un grand nombre de domaines (collections, IO, reflections, programmation fonctionnelle, etc ...) et est souvent déjà présente dans un grand nombre de nos projets.
22 Mar 19:46

Améliorer les performances d'une application standard Spring-Petclinic (partie 5 sur 5)

by Olivier Pitton
Ceci est la 5ème partie de notre série de 5 articles sur l'amélioration de performances de l'application "Spring-Petclinic". Pour améliorer les performances d'une application, l'une des solutions basiques est d'ajouter des caches.
22 Mar 19:46

Les annotations délirantes de Google

by Camille Roux
Google Annotations Gallery est une nouvelle bibliothèque open source proposant des annotations pour que les développeurs puissent s'exprimer dans le code. Vous pourrez ainsi utiliser des annotations dans votre code Java telles que : @CantTouchThis, @Fired, @RTFM, @OhNoYouDidnt...
22 Mar 19:31

Loading a page into a dialog

by Scott González

I previously showed the basic usage of the jQuery UI dialog.  In this article, I’ll show how to open a page in a dialog.  We’ve all been to sites where there’s a help link that opens a popup with some short help text.  This is usually done with a simple window.open call attached to the click event of the link.

<a id="page-help" href="page.html" onclick="window.open(this.href, 'popupwindow', 'width=500,height=300'); return false;">what is this?</a>

Making it better

The one thing that this code has going for it is that users without JavaScript will still be able to get to the page with the help content. Of course, a better implementation would move the JavaScript out of the HTML, properly separating content from behavior.  We can spruce this up by loading the content into a dialog instead of a new window:

$(document).ready(function() {
	$('#page-help').each(function() {
		var $link = $(this);
		var $dialog = $('<div></div>')
			.load($link.attr('href'))
			.dialog({
				autoOpen: false,
				title: $link.attr('title'),
				width: 500,
				height: 300
			});

		$link.click(function() {
			$dialog.dialog('open');

			return false;
		});
	});
});

How it works

We’re finding the link, loading the contents of the linked page into a div and creating a dialog from that div.  We then bind a click event to the link to show the dialog.  This code by itself will work in many situations.  However, the page you’re linking to may have a heading that you’re already reproducing with the dialog title.  There may also be other elements on the page that won’t make sense in a popup, such as navigational elements or a page footer.  Luckily the .load() function allows us to pass in a selector to find the contents that we care about.  In this example we’ll assume the main content of the page is in a div with an id of content.

$(document).ready(function() {
	$('#page-help').each(function() {
		var $link = $(this);
		var $dialog = $('<div></div>')
			.load($link.attr('href') + ' #content')
			.dialog({
				autoOpen: false,
				title: $link.attr('title'),
				width: 500,
				height: 300
			});

		$link.click(function() {
			$dialog.dialog('open');

			return false;
		});
	});
});

View this example in a new window