Les exceptions Java

Les exceptions en Java constituent un mécanisme puissant de traitement des erreurs aussi bien dans une phase d’exécution normale de l’application que dans les phases de débogage. J’aime comparer Java et Pascal ou C. Dans le premier, tout événement anormal (utilisation d’un pointeur null ou un problème d’entrée/sortie par exemple) doit provoquer un arrêt du flot d’exécution… immédiatement ! Alors que dans le second, il peut se passer de nombreuses instructions entre l’origine d’un problème et sa manifestation, rendant le débogage plus difficile.

La perception des exceptions par le programmeur est malheureusement souvent mauvaise. Le programmeur ressent la gestion des exceptions comme une contrainte et s’en débarrasse en la masquant ou en la laissant remonter, par paresse. Cela entraîne un mauvais usage des exception et c’est bien la mauvaise gestion des exceptions en Java par le programmeur qui font dire à certain que java ne sait pas gérer les exceptions.

Pourtant il suffit de respecter quelques principes simples pour tirer le plus grand bénéfice de ce mécanisme de gestion d’erreur.

Une exception doit être rattrapée si on sait comment la traiter

Par traitement on entend une action spécialement prévue dans le cas de l’exception. Par exemple une IOException pourrait être traitée en réessayant un certain nombre de fois ou en demandant à l’utilisateur de changer un paramètre (chemin de fichier par exemple). Mais attention, rattraper une exception ne suffit pas à la traiter correctement. Il ne suffit pas d’encadrer une un appel pouvant lever une exception avec un try catch pour s’assurer d’une bonne gestion. En effet, considérons le code suivant:

instruction1();  // lève une exception
instruction2()

Il ne suffit pas de faire:

try {
  instruction1();
}
catch (Exception e){
  // on gère l'exception
}
instruction2();

Le code précédent assume que la gestion de l’exception doit permettre l’exécution de instruction2(), mais est-ce que la gestion de l’exception le fait réellement ? un simple log sera bien sûr insuffisante, ne vaudrait-il pas mieux ne pas exécuter instruction2() ? Ou bien mettre la 2e instruction dans le bloc try également ? La réponse dépasse le cadre de la gestion des exceptions pour entrer dans celui de la robustesse du code.

Si on ne sait pas comment réagir face à l’exception la solution la plus simple est de ne pas la rattraper.

Sinon on ne rattrape pas et on la déclare

Déclarer l’exception signifie que la méthode doit la porter dans sa signature. C’est le point le plus important. Déclarer l’exception permet de la remonter correctement. Le code appelant qui peut être la méthode main() devra alors gérer l’exception, ce qui nous garantit un traitement correcte de l’exception: au moins par un arrêt du flot d’exécution.

Trop d’exceptions à déclarer: on encapsule

Les programmeurs non habitués à la gestion des exceptions seront alors gênés de devoir déclarer de nombreuses exceptions en signature de méthode.  Il faut alors prévoir une hiérarchie d’exception applicative plus simples qui va permettre d’encapsuler des exceptions plus techniques afin de limiter le nombre d’exception à déclarer dans les méthodes. Le programme va alors devoir rattraper les exceptions (technique ou de base) afin de les encapsuler dans des exceptions applicatives.

Cas des conteneurs d’application

Arrêter le flot d’exécution voire même la JVM est possible quand le programme est seul au monde. Dans le cas d’application web ou de conteneur d’EJB, le code ne doit pas arrêter la JVM car il s’inscrit dans un serveur d’application: il n’est pas seul au monde. Les conteneurs (tel que tomcat) sont bien écrits et une application ne peut pas arrêter la JVM à cause d’une exception qui remonterait toute le pile d’appel. Il y aura un appel qui va rattraper l’exception et en faire quelque chose comme par exemple l’écrire sur la sortie de la JSP. Cela est souvent vu comme une mauvaise chose pour l’utilisateur mais doit être vue comme une bénédiction par le programmeur. C’est la solution minimale qui assure un débogage de l’application.

Empiler ou découpler ?

Un reproche fait aux piles d’exception de Java c’est sa verbosité. C’est un reproche qui peu s’entendre. En effet prenons le cas d’une application multi-tiers où une erreur d’accès à la base de donnée remonte jusqu’à la couche de présentation sous forme d’une pile d’appel qui dévoile toutes les couches de l’application: action de l’utilisateur, appel de service, appel à la couche de persistance et finalement une belle erreur JDBC avec un code obscure en provenance du SGBD. Vous connaissez ce scénario  ? Il arrive quand on laisse filer l’exception ou que l’on encapsule en faisant référence à l’exception original via un appel à :

public Exception(Throwable cause)

Cette façon de faire permet de conserver l’exception d’origine et ainsi de ne pas perdre la pile d’exécution au moment de la levé de l’exception d’origine. C’est la façon de faire la plus sûr et la plus simple. c’est de l’empilage d’exception. Si on ne souhaite pas remonter dans les couches supérieures (appelantes) la pile d’exécution car on considère que cela n’est pas de leur ressort, on peut découpler i.e simplement lever une exception à destination des couches supérieures sans passer l’exception qui en est la cause mais alors il faut tracer (loguer) la cause de manière complète i.e. loguer la pile d’exécution. Ainsi on résout de manière élégante le problème des piles d’erreurs trop verbeuses dans la log des couches supérieurs tout en gardant dans les log des couches inférieurs la trace complète des exceptions à des fin de debug. Cette technique sera particulièrement efficace quand on choisit de mettre en œuvre des log par couche d’abstraction: une log pour la couche de présentation, une log pour la couche métier et une log pour la couche de persistance… ce qui nous amène à l’épineux problème de la gestion des log et c’est une autre histoire.

 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.