mercredi 27 janvier 2016

builder-maven-plugin 1.0 en ligne !

Le plugin maven builder-maven-plugin est enfin déployé sur le repo central maven. La documentation est disponible ici :
https://javabuild.java.net/

vendredi 26 juin 2015

Parce qu'il y en marre d'écrire des pom.xml de 2000 lignes

Si comme moi vous développez en java, il y a des chances que vous perdiez pas mal de temps à écrire et à tester des énormes fichiers pom.xml (maven). Vous me direz qu'avec Gradle, on remplace le xml par du Groovy déjà moins verbeux et plus souple, c'est pas faux. Cela dit à mon avis ce serait 100 fois plus simple pour un développeur java de pouvoir écrire son build tout simplement en java.

J'ai voulu tester l'idée donc j'ai fait un poc de plugin maven qui permet de plugguer des classes java custom directement dans les phases de build maven. Il suffit de placer ces classes dans le projet dans un dossier src/build/java puis d'insérer une annotation pour indiquer quand maven doit les exécuter dans le lifecycle.

Ex :




En fin de compte, on garde dans le pom.xml les infos essentielles du projet (dépendances, scm...) mais pour tout ce qui est plus "custom", plus besoin de s'arracher les cheveux avec un plugin maven compliqué et mal documenté, plus besoin de maven-antrun-plugin (beurk !), on code directement en java dans le projet ex :
  • générer des classes java
  • scanner le projet à la recherche de certaines annotations (ce qu'on fait habituellement au runtime avec Spring et qui coute super cher en temps de démarrage)
  • générer des pages de doc
  • lancer une base embarquée avant les tests unitaires
  • utiliser les nombreuses librairies qui existent et permettent de faire toutes sorte de choses mais n'ont pas de plugin maven
  • tout autre chose qu'on ne peut pas faire facilement avec un plugin maven existant
  • ...
Le code est ici avec un exemple : https://github.com/fxbonnet/builder

Si ça intéresse suffisamment de monde (le nombre de ★ dans github faisant foi), je le déploierai sur le repository central maven.

mercredi 15 avril 2015

Peut-on partager une connection JDBC entre plusieurs threads ?

Pourquoi faire ?
J'ai une application web qui fait de la lecture seule sur une base de données. Donc pas besoin de transactions, d'isolation et tout ça... Dans ce cas pourquoi s’embêter à gérer un pool de connection, ce serait plus simple d'utiliser une seule connection partagée par tous les threads de mon application.

Réponse :
D'autres s'étaient posé la question avant moi. En recherchant sur Google, la réponse est oui, mais...

J'ai trouvé plein de discussions sur le sujet. En synthèse :
  • la spec JDBC dit que les connections doivent être thread-safe mais pas de précisions sur les use-cases
  • en pratique tout le monde dit que le bon pattern c'est une connection par thread et que certains drivers ne sont sans doute pas thread-safe
  • selon les documentations, au moins dans les drivers Oracle et PostgreSQL les connections sont thread-safe (je ne sais pas pour les autres SGBD)
  • en pratique c'est implémenté à grands coups de synchronized donc pour les performances, c'est mort : les requêtes lancées par les différents threads vous s'exécuter l'une après l'autre et non en parallèle, la connection va devenir le goulet d'étranglement de l'application
Donc on peut le faire, mais il ne faut pas le faire. Mieux vaut le pattern classique 1 connection par thread.

Quelques liens intéressants sur le sujet :

lundi 21 octobre 2013

Expressions régulières en Java, greedy quantifier, reluctant quantifier et performances

Les expressions régulières sont très pratiques pour analyser ou filtrer des chaines de caractères. Un exemple type est de repérer tous les tags html présents dans une chaine de caractères. L'expression régulière la plus simple à laquelle on peut penser pour ça est :
<.*>

Maintenant un petit programme java pour tester :
String stringToAnalyze = "<html><head><title>Title</title></head><body>The body</body></html>";
Pattern pattern = Pattern.compile("<.*>");
Matcher matcher = pattern.matcher(stringToAnalyze);
while (matcher.find()) { 
   System.out.println(matcher.group());
}

Problème, en sortie je récupère le résultat suivant :
<html><head><title>Title</title></head><body>The body</body></html>

En fait l'expression régulière va essayer de matcher la chaîne de caractères la plus grande possible (* est ce qu'on appelle un "greedy quantifyer"), ici ce sera toute ma chaîne initiale. Pour éviter ça, deux solutions :

  1. Préciser que la chaîne ne doit pas contenir le caractère '<'. L'expression devient : <[^>]*>
  2. Utiliser un "reluctant quantifier" à la place du "greedy quantifier  pour rechercher cette fois-ci la chaîne la plus courte possible. L'expression devient : <.*?>
Dans les deux cas, j'obtiens le résultat attendu :
<html>
<head>
<title>
</title>
</head>
<body>
</body>
</html>

Laquelle de ces deux solutions est la plus performante ? Personnellement, je parierais sur la deuxième qui me parait plus concise et naturelle donc devrait permettre au compilateur de bien optimiser.

Faisons un petit test Junit pour comparer :

public class RegexPerfTest extends TestCase {

 private static final int nbOfIterations = 10000;
 private static String HTML = "<html><head><title>Title</title></head><body>The body</body></html>";
 static {
  // Build a big html string to test
  for (int i = 0; i < 10; i++) {
   HTML += HTML;
  }
 }

 public void testGreedyQuantifier() throws Exception {
  testPattern("<[^>]*>");
 }

 public void testReluctantQuantifier() throws Exception {
  testPattern("<.*?>");
 }

 public void testPattern(String patternString) throws Exception {
  Pattern pattern = Pattern.compile(patternString);
  for (int i = 0; i < nbOfIterations; i++) {
   Matcher matcher = pattern.matcher(HTML);
   while (matcher.find()) {
    // Do nothing
   }
  }
 }

Résultat :

Conclusion :
  1. l'ordre de grandeur est le même
  2. contre toute attente, le greedy quantifier est un peu plus performant que le reluctant quantifier

mercredi 27 mars 2013

Première réunion des contributeurs ESIgate

Le premier événement réunissant les contributeurs ESIGate (ou motivés pour le devenir) s'est tenue chez Smile (Levallois Perret) ce lundi 25 mars 2013. Au programme une rétrospective sur une année 2012 riche en événements et la roadmap pour 2013.
Ça fait plaisir de voir la communauté qui se développe autour du projet.

La présentation est disponible ici : http://fr.slideshare.net/nricheton/esi-gate-dev-meeting-25-032013

lundi 26 novembre 2012

Chrome ne réspecte plus les Keep-alive timeout

Il y a quelques jours j'ai eu l'occasion de faire un peu de debug réseau, à chaque fois je suis surpris par la créativité des éditeurs de nos navigateurs préférés. Chaque nouvelle version apporte de nouvelles choses au niveau de la gestion des connections afin d'améliorer encore la rapidité coté navigateur mais qu'il faut ensuite gérer coté serveur.

La dernière innovation de Chrome est le choix de garder des connexions ouvertes vers le serveur pendant très longtemps (plusieurs dizaines de minutes) au mépris de la durée de timeout spécifiée par le serveur.

Tout d'abord un petit rappel :

Que disent les spécifications ?

Les navigateurs communiquent avec les serveurs via des connexions TCP.
Encore aujourd'hui, deux versions de la spécifications cohabitent HTTP/1.0 et HTTP/1.1 bien que le premier soit des moins en moins utilisé.

En HTTP/1.0 tout était simple : quand l'utilisateur chargeait une page, le navigateur ouvrait une nouvelle connexion vers le serveur, envoyait la requête, le serveur envoyait la réponse et fermait la connexion.
Problème : l'ouverture d'une nouvelle connexion pour chaque fichier téléchargé prend du temps car ouvrir la connexion nécessite l'échange de 3 paquets réseau (en https c'est même beaucoup plus). Le chargement d'une page complète qui contient beaucoup d'éléments (images, css...) est lent.

En HTTP/1.1 a été inventé le Keep-alive : le navigateur peut demander au serveur via un en-tête HTTP  à garder la connexion ouverte pour la réutiliser pour plusieurs requêtes successives.

Connection: Keep-Alive

Le serveur indique alors par un autre en-tête s'il accepte et si oui sous quelles conditions (durée maximale d'inactivité de la connexion et nombre maximal de requêtes à envoyer par la même connexion).

Keep-Alive: timeout=10, max=5

La spécification indique aussi le nombre maximum de connexions qu'un navigateur à le droit d'ouvrir vers un serveur : 2

Par exemple pour télécharger une page qui contient 20 ressources (images, css, scripts) :

  • un navigateur HTTP/1.0 va ouvrir 20 connections successives qui vont être refermées immédiatement
  • un navigateur HTTP/1.1 va ouvrir 2 connections qu'il va garder ouvertes jusqu'à avoir chargé tous les fichiers et même 10 s de plus (timeout) au cas ou la page charge encore des fichier ou si l'utilisateur navigue sur une autre page.
Le HTTP/1.1 est donc plus rapide pour l'utilisateur. La seule contrainte est la présence de connexion avec une durée de vie plus longue qu'il faut gérer coté client et coté serveur.

En pratique que font les navigateurs

Au départ les éditeurs des navigateurs ont joué le jeu et respecté les spécification HTTP/1.1 à la lettre pour profiter des améliorations de performances mais assez rapidement ils ont voulu aller plus loin en ouvrant plus de connexions en parallèle, d'abord 4 puis parfois 12 ou plus dans les dernières versions !

Google Chrome vient d'aller encore un cran plus loin en ne respectant plus le timeout spécifié par le serveur.
En pratique Chrome va ouvrir plusieurs connexions (a priori jusqu'à 6) mais au lieu de les refermer après le timeout, il va les conserver tant que l'utilisateur garde une fenêtre ouverte sur le site. Selon le système d'exploitation, il va même tenter des les maintenir en vie en envoyant des paquets vides à intervalle régulier.

Ces nouveaux comportements sont à prendre en considération lorsqu'on configure une serveur ou qu'on suit ses performances puisque désormais on va avoir des connexions avec une durée de vie très longue et ce même si les clients n'envoient aucune requête au serveur.



vendredi 15 juin 2012

D'ou vient ce ";jsessionid=..." dans l'URL de la page

Dans de nombreux sites web développés en Java, on voit apparaître parfois un peu n'importe ou un paramètre "jsessionid". Ex :
http://www.legifrance.gouv.fr/rechSarde.do;jsessionid=5D1D8F2EA5F4E3B6B7058B65817EE539.tpdjo09v_2?reprise=true&page=1&lettre=null

Parfois ils apparaissent dans une URL provenant d'une redirection (en général quand on arrive sur le site) mais on les trouve aussi dans les URL des liens qui se trouvent dans les pages. Souvent ces derniers disparaissent quand on rafraîchit la page.

Beaucoup de développeurs ne savent pas vraiment pourquoi ils sont la, quel est leur impact et ne s'en soucient guère. Et pourtant il faut savoir que cette fonctionnalité typique des serveurs d'applications java jadis très utile est aujourd'hui une belle source de problèmes.

Comment ça marche ?

La spécification servlet prévoit que pour pouvoir maintenir coté serveur et état lié à l'utilisateur (la session) le serveur doit implémenter au minimum deux techniques :
  • un cookie de session dont le nom est "JSESSIONID"
  • la réécriture des URL avec l'ajout d'un paramètre "jsessionid" ajouté en principe à la fin de l'URL sous la forme ";jsessionid=".
Mais dans quel cas le serveur va t'il utiliser l'une ou l'autre des techniques ?

Tout d'abord la création de la session :
Tout le monde sait qu'elle a lieu quand dans le code on appelle la méthode request.getSession(). Ce que certains ne savent pas c'est qu'elle a aussi lieu quand on passe dans une jsp dans laquelle on a pas spécifié dans la directive page qu'il n'y avait pas besoin de session. Ex : <%@page session="false" %>
Si on n'y fait pas attention, il y a donc souvent des sessions créées inutilement.

Une fois la session créée, le serveur va faire tout son possible pour maintenir cette session il va donc :
  1. envoyer le cookie JSESSIONID
  2. ajouter le paramètre jsessionid dans les URL à condition que celles-ci passent par la méthode response.encodeURL(...) ou response.sendRedirect(...)
La réécriture d'URL est prévue à l'origine pour les navigateurs qui n'acceptent pas les cookies. Initialement à la création de la session, le serveur ne sait pas si le navigateur acceptera son cookie, il va donc bien activer les deux mécanismes simultanément pour être certain de ne pas perdre la session (en théorie).

Ensuite dans les requêtes suivantes, le serveur s'il reçoit le cookie saura qu'il n'est plus nécessaire de réécrire les URL. Le fonctionnement est donc simple coté serveur :
  • s'il y a un cookie de session dans la requête et le paramètre dans l'URL, on peut désactiver la réécriture
  • s'il y a un cookie de session mais par le paramètre dans l'URL, même chose
  • s'il y a uniquement le paramètre dans l'URL mais pas le cookie, il faudra continuer à réécrire toutes les URL tout au long de la navigation pour cet utilisateur !

Quel impact ?

Le mécanisme de réécriture d'URL va donc s'activer à l'arrivée d'un utilisateur sur le site mais aussi si par malheur un utilisateur refuse les cookies... ce qui est le cas pour les crawlers des moteurs de recherche comme Google, résultat : de nombreuses pages sont indexées par les moteurs de recherche avec un jsessionid. Elles peuvent aussi être mises en cache par des proxy.
On peut facilement vérifier ceci en saisissant des Google "inurl:;jsessionid". On obtient 216 000 000 de résultats !

Mais que fait le serveur quand un utilisateur arrive avec l'une de ces URL par accident (à cause d'un cache ou d'un moteur de recherche) ?
Si la session correspondante a expiré, une nouvelle session va être créée avec un nouvel identifiant. Mais si par contre la session est encore valide, on peut se retrouver avec plusieurs internautes qui vont partager la même session.

Autre cas de figure : dans le cas ou l'application est déployée sur une cluster, généralement avec affinité de session, le cookie comporte généralement deux partie dont la première est l'identifiant de session suivi par l'identifiant du noeud cible. Ex : ;jsessionid=5D1D8F2EA5F4E3B6B7058B65817EE539.tpdjo09v_2
Ceci a pour but qu'un même utilisateur soit toujours dirigé vers le même serveur pour conserver sa session. Mais si encore une fois de nombreux utilisateurs arrivent via cette même URL, ils seront tous dirigés vers le même noeud du cluster, entraînant un déséquilibre de charge.

Dans le cas d'un proxy qui fait aussi du cache, afin d'éviter ce genre de problèmes il faudrait pour bien faire refuser de mettre en cache les réponses dès lors qu'elles contiennent un en-tête "Set-cookie" puisqu'elle peuvent correspondre au moment de la création de session mais aussi repérer dans les requête les jsessionid pour ne pas non plus mettre en cache les résultats qui correspondront potentiellement au cas de l'utilisateur qui n'accepte pas les cookies.
On pourrait également imaginer filtrer les jsessionid directement dans les pages mais ce serait complexe et coûteux.

Il faut noter que dans le cas hypothétique ou un utilisateur s'amuserait réellement à naviguer sans cookie, il suffit d'un seul lien que les développeurs auraient oublié de réécrire (par exemple dans une application développée avec le framework Struts un lien en dur plutôt que généré par un tag Struts) pour que l'utilisateur perde sa session !

Conclusion

Le mécanisme de suivi de session par réécriture d'URL partait d'une bonne intention. Conçu à une époque ou certains navigateurs ne supportaient pas les cookies et ou certains utilisateurs les désactivaient volontairement.
De nos jours il semble impensable de naviguer sans cookie et d'ailleurs même sur les sites ou la réécriture fonctionne, il n'y en a pratiquement aucun pour lequel la réécriture soit systématique.
C'est donc devenu une mécanisme pratiquement inutile mais source de nombreux problèmes et qu'aucune option ne permet de désactiver.

En pratique certains serveurs proposent quand même de les désactiver même si ce n'est pas standardisé. Une autre solution est d'utiliser un Servlet Filter.
Pour les éditeurs de proxys, de caches ou de crawlers ça reste un gros problème.