Levée d'exception & singleton

Levée d'exception & singleton - Java - Programmation

Marsh Posté le 07-01-2004 à 14:28:57    

Chalut,
 
Il y a une question que je me pose depuis un moment et à laquelle je n'ai pas encore trouvé de "best practice", ça concerne la levée d'exception lors de la phase d'instanciation d'un singleton.
 
Dans mon exemple, je veux créer une classe AppProperties de type singleton qui permet de centraliser mes constantes (chargées depuis un fichier de properties). Pour lire la valeur des constantes, je fais:
 

Code :
  1. AppProperties.getSingleton().getProperty("toto.tutu" )


 
Ma méthode statique getSingleton() me retourne mon instance de AppProperties en la créant si nécessaire en appelant le constructeur, lequel appel une méthode init(). Cette méthode lit depuis un fichier le paramétrage, je peux donc avoir des IOException qui sont levées.
 
Comment puis-je gérer la levée d'exception lors de l'exécution de la méthode init() ??? Je me vois mal propager l'exception au constructeur qui la relancerait, ça me semble un peu crade... D'un autre côté, je vois mal l'exception relancée par méthode statique getInstance()...
 
Comment gère-t-on ce genre de problème ??? Est-ce-que je m'y prends mal ??? Dois-je faire un System.exit(-1) en cas de levée d'exception ???

Reply

Marsh Posté le 07-01-2004 à 14:28:57   

Reply

Marsh Posté le 07-01-2004 à 14:51:00    

Tout d'abord, dans le principe, un constructeur peut tout à fait lever une exception, c'est tout sauf crade : comment sinon indiquer en "retour" du new que l'objet n'a pas pu être correctement construit ?
 
Maintenant, le problème, c'est que si tu as 50 singletons différents, les 50 sont peut-être susceptibles de lever une exception, et chacune est susceptible de bloquer le reste du programme. Cependant, n'oublie jamais que ton singleton n'est pas destiné à être toujours appelé depuis une application Java. Demain, tu l'inclueras peut-être à une applet ou une servlet. Il est largement préférable que seule la classe du main() se permette de faire des System.exit().
 
Ce que je peux te proposer, c'est de te définir une classe standard SingletonInitializationError, qui hériterait de Error, par exemple. Le constructeur pourrait alors lever une exception, que le getInstance() propagerait sous la forme d'une SingletonInitializationError jusqu'au niveau où ce type d'exception est traitable : le main() dans une application, le run() dans une applet, un thread ou une servlet.

Reply

Marsh Posté le 07-01-2004 à 14:59:11    

BifaceMcLeOD a écrit :

Tout d'abord, dans le principe, un constructeur peut tout à fait lever une exception, c'est tout sauf crade : comment sinon indiquer en "retour" du new que l'objet n'a pas pu être correctement construit ?


 
OK, je trouvais ça crade car je n'avais jamais croisé ce cas de figure, ça me semblait bizzarre. A savoir...
 
[citation]
Maintenant, le problème, c'est que si tu as 50 singletons différents, les 50 sont peut-être susceptibles de lever une exception, et chacune est susceptible de bloquer le reste du programme. Cependant, n'oublie jamais que ton singleton n'est pas destiné à être toujours appelé depuis une application Java. Demain, tu l'inclueras peut-être à une applet ou une servlet. Il est largement préférable que seule la classe du main() se permette de faire des System.exit().[/citation]
 
Oui, en effet, je me disais bien que le System.exit() était à proscrire. Ton explication est très censée  :jap:  
 
[citation]
Ce que je peux te proposer, c'est de te définir une classe standard SingletonInitializationError, qui hériterait de Error, par exemple. Le constructeur pourrait alors lever une exception, que le getInstance() propagerait sous la forme d'une SingletonInitializationError jusqu'au niveau où ce type d'exception est traitable : le main() dans une application, le run() dans une applet, un thread ou une servlet.
[/citation]
 
Je ne connaissais même pas l'existence de la classe Error! Je viens encore d'apprendre quelquechose.  
 
En tant cas, merci BifaceMcLeOD, tes réponses sont toujours très claires et précises  :jap:


Message édité par machinbidule1974 le 07-01-2004 à 14:59:38
Reply

Marsh Posté le 07-01-2004 à 15:11:23    

Juste un détail, en java 1.4 je signale l'existance du package java.util.prefs et particulièrement la classe java.util.prefs.Preferences
 
http://java.sun.com/j2se/1.4.2/doc [...] mmary.html
 
c'est très pratique.


---------------
trainoo.com, c'est fini
Reply

Marsh Posté le 07-01-2004 à 15:15:16    

Je ne connaissais pas non plus. Malheureusement, mon JRE n'est qu'en v1.3.1
 
Ce sera utile sur d'autres projets

Reply

Marsh Posté le 07-01-2004 à 15:16:19    

D'après le JDK, il y a 3 catégories d'exceptions :

  • celles qui héritent de java.lang.Exception, qui indiquent des cas d'erreur qu'une application "raisonnable" peut vouloir traiter. Celles-là doivent être déclarées dans les clauses "throws".
  • celles qui héritent de java.lang.RuntimeException, qui sont des cas d'erreur considérés comme normaux mais exceptionnels dans une exécution correcte. Celles-là n'ont pas besoin d'être déclarées dans les clauses "throws".
  • celles qui héritent de java.lang.Error : celles-là indiquent des cas d'erreurs suffisamment graves (voire fatales) pour qu'une application "raisonnable" (dixit encore le JDK) ne les traite pas. Elles n'ont pas non plus besoin d'être déclarées dans les clauses "throws".

Ceci dit, ce n'est pas parce que le JDK conseille de ne pas traiter les erreurs qu'il ne faut pas le faire, au moins pour certaines. Ainsi, si tu écris un serveur en Java, ou même une application graphique un peu gourmande, il est recommandé de traiter les éventuelles java.lang.OutOfMemoryError. Normalement, ça ne devrait jamais arriver, mais si jamais ça arrive, il est de bon ton que le serveur ou l'application sache en survivre...


Message édité par BifaceMcLeOD le 07-01-2004 à 15:17:02
Reply

Marsh Posté le 07-01-2004 à 15:24:26    

machinbidule1974 a écrit :

Je ne connaissais pas non plus. Malheureusement, mon JRE n'est qu'en v1.3.1
 
Ce sera utile sur d'autres projets  


Si tu n'es pas en JDK 1.4, alors les exceptions ne sont pas chaînables. Dans ce cas, je te conseille de permettre de lier un SingletonInitializationError à une autre exception :

Code :
  1. public class SingletonInitializationError {
  2.   private Throwable  chainedThrowable;
  3.   ...
  4.   public SingletonInitializationError(Throwable thr) {
  5.     this.chainedThrowable = thr;
  6.   }
  7.   public String getMessage() {
  8.     return this.chainedThrowable.getMessage();
  9.   }
  10.   public void printStackTrace(PrintStream stream) {
  11.     this.chainedThrowable.printStackTrace(stream);
  12.   }
  13.   .... // etc.
  14. }


Ainsi cette sous-classe d'Error n'est plus qu'une encapsulation de l'exception réellement levée, ce qui permet d'uniformiser la récupération de l'exception sous-jacente tout en limitant clairement la portée de cette récupération.


Message édité par BifaceMcLeOD le 07-01-2004 à 15:25:10
Reply

Marsh Posté le 07-01-2004 à 15:33:19    

BifaceMcLeOD a écrit :

D'après le JDK, il y a 3 catégories d'exceptions :

  • celles qui héritent de java.lang.Exception, qui indiquent des cas d'erreur qu'une application "raisonnable" peut vouloir traiter. Celles-là doivent être déclarées dans les clauses "throws".
  • celles qui héritent de java.lang.RuntimeException, qui sont des cas d'erreur considérés comme normaux mais exceptionnels dans une exécution correcte. Celles-là n'ont pas besoin d'être déclarées dans les clauses "throws".
  • celles qui héritent de java.lang.Error : celles-là indiquent des cas d'erreurs suffisamment graves (voire fatales) pour qu'une application "raisonnable" (dixit encore le JDK) ne les traite pas. Elles n'ont pas non plus besoin d'être déclarées dans les clauses "throws".

Ceci dit, ce n'est pas parce que le JDK conseille de ne pas traiter les erreurs qu'il ne faut pas le faire, au moins pour certaines. Ainsi, si tu écris un serveur en Java, ou même une application graphique un peu gourmande, il est recommandé de traiter les éventuelles java.lang.OutOfMemoryError. Normalement, ça ne devrait jamais arriver, mais si jamais ça arrive, il est de bon ton que le serveur ou l'application sache en survivre...


 
Je connaissais les 2 premières mais par la 3ème catégorie... Ceci dit comment peux-tu traiter les OutOfMemoryError dans une application ? En appelant le garbage collector explicitement ?

Reply

Marsh Posté le 07-01-2004 à 18:38:59    

Imagine que dans une application avec une interface graphique, l'utilisateur te demande d'ouvrir un fichier très très gros, et que son chargement -- et sa transformation en la représentation interne du programme -- déclenche une OutOfMemoryError. Il suffit alors d'interrompre le chargement en cours de route, et d'afficher une jolie boite de dialogue d'excuse à l'utilisateur.
Si, en écrivant le programme, tu as bien fait attention de ne pas garder de référence sur les objets chargés avant la fin du chargement (hors variables locales, bien sûr), l'interruption du chargement en cours de route libérera la mémoire prise par les objets temporaires presque immédiatement, sans même appeler explicitement le GC.
 
edit> Error hérite de Throwable et non d'Exception, c'est pour cela qu'elle est beaucoup moins connue. Et puis il faut quand même admettre que c'est plutôt rare de créer des sous-classes d'Error !  ;)


Message édité par BifaceMcLeOD le 07-01-2004 à 18:41:31
Reply

Marsh Posté le 07-01-2004 à 19:05:57    

BifaceMcLeOD a écrit :

Ce que je peux te proposer, c'est de te définir une classe standard SingletonInitializationError, qui hériterait de Error


pourquoi pas de RuntimeException plutot ??? ca me parait plus adapté ...


---------------
ma vie, mon oeuvre - HomePlayer
Reply

Marsh Posté le 07-01-2004 à 19:05:57   

Reply

Marsh Posté le 07-01-2004 à 19:07:28    


je connaissais pas non plus (j'ai pas encore nivestigué le 1.4  :whistle: ).
Ca sauve ca en registry sous win ?


---------------
ma vie, mon oeuvre - HomePlayer
Reply

Marsh Posté le 07-01-2004 à 19:17:51    

benou a écrit :


je connaissais pas non plus (j'ai pas encore nivestigué le 1.4  :whistle: ).
Ca sauve ca en registry sous win ?

Je pense pas, plutôt dans le truc des applications de l'utilisateur.  
Sous OSX ça sauve au bon endroit en tout cas :

Code :
  1. nraynaud@macaron:~$ cat /Users/nraynaud/Library/Preferences/org.njchat.njchat.plist
  2. <?xml version="1.0" encoding="UTF-8"?>
  3. <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  4. <plist version="1.0">
  5. <dict>
  6.         <key>/org/njchat/njchat/</key>
  7.         <dict>
  8.                 <key>defaultIdentity</key>
  9.                 <string>nraynaud</string>
  10.                 <key>defaultServer</key>
  11.                 <string>irc.voila.fr</string>
  12.                 <key>identities/</key>
  13.                 <dict>
  14.                         <key>nraynaud/</key>
  15.                         <dict>
  16.                                 <key>description</key>
  17.                                 <string>je suis moi !</string>
  18.                                 <key>nick</key>
  19.                                 <string>nraynaud</string>
  20.                                 <key>pseudo</key>
  21.                                 <string>nraynaud</string>
  22.                         </dict>
  23.                 </dict>
  24.                 <key>servers/</key>
  25.                 <dict>
  26.                         <key>irc.proprenetworks.net/</key>
  27.                         <dict>
  28.                                 <key>port</key>
  29.                                 <string>6667</string>
  30.                         </dict>
  31.                         <key>irc.voila.fr/</key>
  32.                         <dict>
  33.                                 <key>port</key>
  34.                                 <string>6667</string>
  35.                         </dict>
  36.                 </dict>
  37.         </dict>
  38. </dict>
  39. </plist>

Chez Apple ils sont fans de ce format (et la classe concrête est bien une classe d'Apple).


---------------
trainoo.com, c'est fini
Reply

Marsh Posté le 08-01-2004 à 10:19:28    

benou a écrit :


pourquoi pas de RuntimeException plutot ??? ca me parait plus adapté ...


Question de gravité d'erreur. Si l'on suit les définitions de 3 catégories d'exceptions, l'erreur d'initialisation d'un singleton ne relève pas des erreurs "normales" dans la vie d'un programme, donc serait plutôt une Error plutôt qu'une RuntimeException. Mais c'est une distinction assez subtile, je te l'accorde.

Reply

Marsh Posté le 08-01-2004 à 10:22:13    

ouais je trouve aussi.
 
Pour moi Error c'est des erreurs "grave de chez grave" ... J'ai toujuors fait hérité mes exceptions d'initialisation de Runtime.
 
Un des raisons à ca c'est que si quelque part je catch Exception, je considère que je suis en droit de récupérer une erreur d'inititialisation non prévue ...


---------------
ma vie, mon oeuvre - HomePlayer
Reply

Marsh Posté le 08-01-2004 à 10:27:15    

BifaceMcLeOD a écrit :


Question de gravité d'erreur. Si l'on suit les définitions de 3 catégories d'exceptions, l'erreur d'initialisation d'un singleton ne relève pas des erreurs "normales" dans la vie d'un programme, donc serait plutôt une Error plutôt qu'une RuntimeException. Mais c'est une distinction assez subtile, je te l'accorde.

Là non, il peut continuer avec des paramètres pris ailleur.
Error c'est des trucs style : ÇaVaCouperChérieError ou tout au plus tu peux essayer de dire "ça va trancher chérie !" avant que tout parte en vrille. Alors qu'avec une PneuDeLaChaussureÉclatéException qui dérive de RuntimeException on peut continuer.


Message édité par nraynaud le 08-01-2004 à 10:28:20

---------------
trainoo.com, c'est fini
Reply

Marsh Posté le 08-01-2004 à 15:19:33    

Oui sauf que comme je le disais plus haut, on peut très bien récupérer une OutOfMemoryError sans avoir aucune instabilité ensuite dans la JVM...
 
Par ailleurs, benou, ta supposition est fausse. De nombreux articles de gourous te confirmeront que catcher Exception ne suffit pas quand on veut écrire du logiciel robuste : il faut catcher Throwable.

Reply

Marsh Posté le 08-01-2004 à 15:25:36    

BifaceMcLeOD a écrit :

Par ailleurs, benou, ta supposition est fausse. De nombreux articles de gourous te confirmeront que catcher Exception ne suffit pas quand on veut écrire du logiciel robuste : il faut catcher Throwable.


Je pars du principe que c'est intéressant de catcher une exception que si on sait quoi en faire. Catcher Throwable à tout bout de champ c'est n'importe quoi ! Qu'est ce que tu peux faire d'une exception dont tu ne connais rien ?
 
Ca peux servir dans certains cas, mais c'est quand même plutot rare ...
Faire un catch Throwable pour récupérer un singleton contenant des propriétés ca me paraitrait plutot louche :/
 
Nan vraiment, je reste sur mon idée du InitException qui étend RuntimeException ...


---------------
ma vie, mon oeuvre - HomePlayer
Reply

Marsh Posté le 08-01-2004 à 15:49:01    

+1 sur beenou, catcher n'importe quoi et continuer dans n'importe quel état et si possible en vrac c'est n'importe quoi et sûrement pas de la robustesse.


---------------
trainoo.com, c'est fini
Reply

Marsh Posté le 08-01-2004 à 16:41:49    

Pas n'importe où. Evidemment, n'importe où, c'est inepte et dangereux. Par contre, en fin de run() dans un thread ou une servlet (avec le try correspondant au tout début), ça ne l'est pas.
Libre au programmeur d'en faire ce qu'il veut après, de son exception, bien sûr (envoyer dans le log, afficher une jolie boite de dialogue "Unexpected exception" si on est dans une appli Swing, etc).


Message édité par BifaceMcLeOD le 08-01-2004 à 16:42:03
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed