52 votes

Comment puis-je passer une collection d'exceptions en tant que cause racine ?

Une méthode, myMethod, invoque plusieurs exécutions parallèles et attend leur achèvement.

Ces exécutions parallèles peuvent se terminer par des exceptions. Ainsi, myMethod obtient une liste d'exceptions.

Je veux transmettre la liste d'exceptions en tant que cause racine, mais la cause racine pourrait n'être qu'une seule exception. Bien sûr, je peux créer ma propre exception pour obtenir ce que je veux, mais je veux savoir si Java, Spring ou Spring Batch propose quelque chose de similaire prêt à l'emploi.

3 votes

.NET a une exception AggregateException qui contient une liste d'exceptions. Cette idée devrait également être applicable à Java.

1 votes

49voto

T.J. Crowder Points 285826

Je ne suis pas sûr que je le ferais (bien que, étant donné la JavaDoc, je ne pourrais pas vous dire pourquoi j'hésite), mais il y a la liste des exceptions supprimées sur Throwable, que vous pouvez ajouter via addSuppressed. La JavaDoc ne semble pas dire que cela est réservé à l'utilisation par la JVM dans les try-with-resources :

Ajoute l'exception spécifiée aux exceptions qui ont été supprimées pour livrer cette exception. Cette méthode est thread-safe et généralement appelée (automatiquement et implicitement) par l'instruction try-with-resources.

Le comportement de suppression est activé sauf s'il est désactivé via un constructeur. Lorsque la suppression est désactivée, cette méthode ne fait rien d'autre que de valider son argument.

Notez que lorsque qu'une exception en provoque une autre, la première exception est généralement capturée et ensuite la deuxième exception est lancée en réponse. En d'autres termes, il y a une connexion causale entre les deux exceptions. En revanche, il existe des situations où deux exceptions indépendantes peuvent être lancées dans des blocs de code enfants, en particulier dans le bloc try d'une instruction try-with-resources et dans le bloc finally généré par le compilateur qui ferme la ressource. Dans ces situations, seule l'une des exceptions lancées peut être propagée. Dans l'instruction try-with-resources, lorsqu'il y a deux exceptions de ce type, l'exception provenant du bloc try est propagée et l'exception du bloc finally est ajoutée à la liste des exceptions supprimées par l'exception du bloc try. Au fur et à mesure que l'exception déroule la pile, elle peut accumuler de multiples exceptions supprimées.

Une exception peut avoir des exceptions supprimées tout en étant causée par une autre exception. Le fait qu'une exception ait une cause est connu sémantiquement au moment de sa création, contrairement au fait qu'une exception va ou non supprimer d'autres exceptions, ce qui est généralement seulement déterminé après qu'une exception est lancée.

Remarquez que le code écrit par le programmeur peut également tirer parti de l'appel de cette méthode dans les situations où il y a plusieurs exceptions frères et où seule l'une peut être propagée.

Remarquez ce dernier paragraphe, qui semble convenir à votre cas.

0 votes

[...] que ce soit ou non une exception va supprimer d'autres exceptions [...] est généralement déterminé uniquement après qu'une exception soit déclenchée. J'imagine que ce ne sera pas le cas lorsque plusieurs exceptions supprimées sont collectées à partir d'exécutions parallèles.

24voto

Joachim Sauer Points 133411

Les exceptions et leurs causes sont toujours uniques : vous pouvez lever une exception et chaque exception ne peut avoir que une cause (qui peut à son tour avoir une cause...).

Cela pourrait être considéré comme une erreur de conception, surtout lorsqu'on considère le comportement multi-thread que vous avez décrit.

C'est l'une des raisons pour lesquelles Java 7 a ajouté addSuppressed à Throwable, qui permet essentiellement d'attacher un nombre arbitraire d'exceptions à une seule autre (la principale motivation étant le try-with-resources qui avait besoin d'une manière de gérer les exceptions dans le bloc finally sans les abandonner silencieusement).

Ainsi, lorsque vous avez 1 exception qui provoque l'échec de votre processus, vous ajoutez celle-ci comme cause de votre exception de niveau supérieur, et s'il y en a d'autres, vous les ajoutez à l'originale en utilisant addSuppressed. L'idée est que cette première exception "supprime" les autres en devenant un membre de la "vraie chaîne d'exceptions".

Exemple de code :

Exception exception = null;
for (Foobar foobar : foobars) {
  try {
    foobar.frobnicate();
  } catch (Exception ex) {
    if (exception == null) {
      exception = ex;
    } else {
      exception.addSuppressed(ex);
    }
  }
}
if (exception != null) {
  throw new SomethingWentWrongException(exception);
}

4 votes

Je ne le ferais pas tout à fait de la manière que vous suggérez, à moins qu'une des exceptions sous-jacentes puisse vraiment être identifiée comme la "principale". Si vous choisissez arbitrairement l'une des exceptions comme principale et les autres comme supprimées, vous invitez un appelant à ignorer les exceptions supprimées et à signaler simplement la principale - même si l'exception "principale" est une TypoInUserInputException et l'une des exceptionssuppprimées est une DatabaseCorruptedException.

1 votes

… Au lieu de cela, je marquerais toutes les exceptions sous-jacentes comme étant supprimées par l'exception SomethingWentWrongException, et je donnerais à cette exception un message indiquant clairement qu'une ou plusieurs exceptions supprimées doivent suivre, par exemple quelque chose comme "X sur Y tâches ont échoué, voir la liste des échecs ci-dessous".

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X