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.

mercredi 13 juin 2012

EsiGate 3.4 : nouveau système de cache

Dans la version 3.4 d'EsiGate, le système de cache développé spécifiquement (à l'époque ou il n'existait pas de cache HTTP en open source Java) a été remplacé par le cache qui est apparu depuis la version 4.1 de Apache HttpClient.

Cette migration en plus de réduire la base de code d'EsiGate d'environ 20% permet d'améliorer la qualité (le projet HttpClient Cache bénéficie d'une bonne communauté et une conception particulièrement soignée), de réduire le travail de maintenance coté EsiGate et apporte quelques fonctionnalités très intéressante dont :

  • support EhCache / Terracotta
  • support MemCached
  • revalidation des entrées de cache en tache de fond (très utile pour les site à forte charge avec un backend peu optimisé)
Plus de détails sur le blog EsiGate

mercredi 30 mars 2011

Faire tourner GWT sur un environnement .NET, qui a dit impossible ?

Après avoir réussi à faire tourner et à compiler des applications Java Spring / Hibernate sous environnement .NET à l'aide de IKVM, l'étape suivante : faire tourner une application web java dans IIS. Pour tester, quel meilleur exemple qu'une application GWT ?

A priori personne n'avait tenté ça avant nous. Les articles trouvés sur internet parlaient d'essais de développement de services RPC dans d'autres technologies avec GWT utilisé uniquement pour la partie client (html et javascript). Notre objectif était différent : développer une interface web en utilisant GWT à la fois pour la partie client (html et javascript) et pour la partie serveur (servlet RPC), donc vraiment un développement standard GWT. Ensuite utiliser IKVM pour compiler cette application en .NET et la transformer en une application web .NET pour la faire tourner dans un serveur IIS.

Deux difficultés :

  1. compiler le code java, en particulier GWT à l'aide de IKVM
  2. faire un pont entre l'API Servlet Java et l'API System.web
Eh bien ça marche !

L'article complet se trouve ici :

mardi 8 février 2011

Article sur ESIGate

Pour ceux qui ne connaitraient pas encore ESIGate, un petit article qui explique ce que c'est :
http://blog.smile.fr/ESIGate-le-portail-d-un-genre-nouveau

Pas évident d'expliquer à quoi ça sert sans trop rentrer dans les détails techniques...
Sinon ESIGate c'est ici :
http://www.esigate.org

Marier Java et .NET à l'aide de IKVM

IKVM, la machine virtuelle Java écrite en .NET semble maintenant être arrivée à maturité. Rappel des principes :

  • partant du constat que le JDK et le framework .NET se ressemblent énormément (les mauvaises langues diront qu'ils se copient mutuellement) IKVM, plutôt que de réimplémenter un JDK à partir de zéro, implémente les fonctionnalités du JDK soit en utilisant la fonctionnalité correspondante du framework .NET soit en réutilisant du code de OpenJDK
  • IKVM tourne sous environnement Microsoft .NET ou Mono mais c'est déjà bien puisque on peut déjà choisir entre les environnements Windows ou Linux
  • IKVM permet de convertir une librairie Java en .NET pour utilisation dans une application .NET (un .jar devient une .dll !)
Il restait à vérifier que l'outil est bien à la hauteur de ses promesses en faisant un proto faisant tourner une applications Java et des librairies Java complexe dans cette environnement. Le choix s'est porté sur une application utilisant Spring et Hibernate. Le résultat est au delà  des espérances :
  • l'application java fonctionne et peut même être convertie en un fichier .exe !
  • un rapide test de performances montre des résultats similaires à la même application tournant avec le JDK Oracle
L'article détaillé se trouve ici :

Prochaine étape : arriver à faire tourner une application web java dans IIS. Les tests en cours sont encourageants.

lundi 2 novembre 2009

Parser une page HTML ou la transformer avec XSL

En Java, quelques outils open-source permettent de traiter comme un fichier xml une page HTML, même mal formée au sens xml (par exemple avec des balises non fermées type "<br>").

J'ai testé un peu les deux outils les plus connus htmlparser et CyberNeko ainsi qu'un troisième que je ne connaissais pas : validator.nu HTML Parser

Ce que j'ai noté :

htmlparser
  • pas de dépendances (pratique pour l'utiliser dans un projet)
  • le parsing se fait via une API propriétaire
  • il permet de construire un document en mémoire qu'on peut ensuite manipuler (pas terrible si on veut s'en servir sur des gros volumes)
  • l'outil peut charger une page directement depuis le web
  • assez pratique à utiliser pour réaliser un crawler de sites
  • projet peu actif (la dernière version stable date de juin 2006, autant dire que le projet est mort)
CyberNeko
  • dépend de Xerces dont il utilise l'API (assez gênant quand on l'utilise dans un projet vu qu'il ramène une version de XercesImpl ainsi que de xml-apis)
  • transforme une page en un document DOM (pas idéal pour les performances)
  • normalise tous les tags en html 4 (noms de tags en majuscules, attributs en minuscules) mais sans notion de namespace. Un peu surprenant.
  • projet actif (la dernière version stable date de septembre 2009)
  • donne de bon résultats en performances d'après le benchmark réalisé par le projet PortletBridge
Validator.nu HTML Parser
  • projet récent et actif
  • implémentation de l'algorithme de parsing standardisé html 5
  • pas de dépendances (pratique pour l'utiliser dans un projet)
  • permet de faire du SAX
  • permet de transformer directement un document via xslt
  • nécessite un parser xml pas trop ancien (ne fonctionne pas par exemple avec le plugin maven cargo et jetty embedded)
  • compilable avec GWT (ça peut servir ?)
  • attribue par défaut aux tags html le namespace xmlns="http://www.w3.org/1999/xhtml" donc il vaut mieux bien connaitre la manipulation des namespaces avant de s'en servir
Personnellement, je préfère largement le troisième qui m'a permis de transformer une page html non conforme xml en seulement quelques lignes de code et sans faire de DOM, uniquement en SAX et sans introduire de dépendances supplémentaires dans mon projet.

mercredi 17 décembre 2008

Squill : ceci n'est pas un ORM

Fichiers xml de mapping, annotations JPA, Hibernate, HQL Criteria... Voila avec quoi doivent jongler les développeurs Java pour accéder à une base de données.

Résultat :
  • un long apprentissage pour maitriser tout ça
  • le développeur est éloigné du SQL, bugs et problèmes de performances sont inévitables
  • la plupart des problèmes ne sont identifiés qu'à l'exécution, en cours de développement, il n'y a aucune vérification de la cohérence des développements avec la structure de la base
Squill propose une approche radicalement différente :
  1. une tache ant génère un ensemble de classe correspondant aux tables de la base
  2. le développeur utilise ces classes qui offrent des méthodes correspondant aux différentes instructions SQL
Les avantages sont énormes :
  • Le développeur code pratiquement du SQL
  • Aucun SQL généré automatiquement
  • Tout est vérifié à la compilation (ex : si on supprime une colonne, on a une erreur de compilation !)
  • Autocompletion
Enfin tout ça, c'est la théorie. Pour l'instant ce n'est que de la version beta, mais j'aime beaucoup l'idée.

lundi 7 juillet 2008

Portlet, bridge et servlet filter

Pour ceux qui se sont arrachés les cheveux à essayer d'intégrer des webapp en portlet via un bridge, il est possible de faire fonctionner les servlet filter dans l'application. Pour cela il faut :
1) être en servlet 2.4 ou +
Il faut donc utiliser le bon schema au niveau du fichier web.xml
Tourner sous un Tomcat 5.5 ou +
2) utiliser l'élément au niveau de chacun des filter pour indiquer s'il doit être exécuté en cas de forward/include
Attention au risque de boucle infinie !

Plus de détails :
http://www.ibm.com/developerworks/java/library/j-tomcat2/#N10082

Extrait de la spec servlet 2.4 :
SRV.6.2.5 Filters and the RequestDispatcher
New for version 2.4 of the Java Servlet specification is the ability to configure filters
to be invoked under request dispatcher forward() and include() calls.
By using the new element in the deployment descriptor, the
developer can indicate for a filter-mapping whether he would like the filter to be
applied to requests when:
1. The request comes directly from the client.
This is indicated by a element with value REQUEST,
or by the absence of any elements.
2. The request is being processed under a request dispatcher representing the
Web component matching the or using a forward()
call.
This is indicated by a element with value FORWARD.
3. The request is being processed under a request dispatcher representing the
Web component matching the or using an include()
call.
This is indicated by a element with value INCLUDE.
4. The request is being processed with the error page mechanism specified in "Error
Handling" on page 73 to an error resource matching the .
This is indicated by a element with the value ERROR.
5. Or any combination of 1, 2, 3, or 4 above.

jeudi 3 juillet 2008

Manipulation des chaines de caractères et performances en Java

En Java, dans des programmes qui doivent manipuler, analyser et traiter des chaines de caractères, la manière de coder peut avoir un gros impact sur les performances. Quelques éléments clé à prendre en compte :
  1. Une String est un objet immuable. Elle peut être utilisée en parallèle par plusieurs Threads simultanément en toute sécurité. Il est donc inutile de copier une chaine de caractère en faisant new String(s).
  2. Inutile de sortir systématiquement vos chaines de caractères comme des constantes, le compilateur le fait pour vous !
  3. Une chaine de caractère contient un char[]. Les opérations de type substring() sont très peu couteuses en mémoire ou en CPU puisqu'il n'y a pas copie de ce tableau mais simplement création d'un nouvel objet String qui pointe vers le même tableau.
  4. A la compilation, une concaténation de chaines de caractères "toto" + "titi" est automatiquement remplacée par la création d'un StringBuilder. Cette classe existe depuis le JDK 1.5 et est bien plus performante que la classe StringBuffer. L'API est la même mais il n'y a pas de synchronized (a ne pas utiliser en accès multithread donc). Donc remplacer dans une méthode les concaténation de chaine par des StringBuffer comme on le recommandait il y a quelques années fait désormais baisser les performances !
  5. Un StringBuilder ou un StringBuffer contient lui aussi un char[] dont la taille par défaut est 16. Dès que le buffer est plein, le char[] est automatiquement remplacé par un char deux fois plus grand dans lequel le premier est copié. Bien dimensionner le buffer à l'origine peut donc économiser pas mal d'opérations de copie.
  6. L'appel de la méthode toString() d'un StringBuilder ou d'un StringBuffer déclenche la copie du char[] qu'il contient. Ne pas faire writer.append(stringBuffer.toString()) mais plutôt directement writer.append(stringBuffer) permet d'éviter une opération de copie.
  7. Toutes les opérations de type replace() ou replaceall() déclenchent des copies du tableau.
  8. On peut exécuter des recherches et des remplacements directement sur des StringBuffer sans faire de toString() de la manière suivante :
    stringBuilder=Pattern.compile("expr").matcher("stringBuilder").replaceAll("newvalue"); On économise ainsi une copie.

Ne pas hésiter à décompiler les classes String et StringBuilder pour bien comprendre comment tout ça fonctionne. C'est très instructif et ça aide beaucoup à optimiser le code.

mardi 24 juin 2008

Mort aux fichiers xml de configuration

En tant que développeur Java, je déteste passer mon temps à écrire des fichiers xml. La plupart des frameworks à la mode demandent d'énormes fichiers de configuration qui ont la facheuse habitude de nécessiter à chaque modification un redémarrage des applications.

Pourquoi ne pas coder tout ça en Java directement ? Pour l'essentiel, il ne s'agit pas de paramétrages mais de choses qui ne changeront jamais dans la vie de l'application. Alors pourquoi supporter un parsing lourd et source de bugs à chaque démarrage de l'application ? Les rares paramètres qui nécessitent réellement d'être changés de temps en temps sont ceux qui dépendent de la configuration de déploiement (machines, adresses, mots de passe...), ils tiendraient dans un properties.