Comment puis-je transmettre une collection d'exceptions comme cause première?

52

Une méthode myMethod,, appelle plusieurs exécutions parallèles et attend leur fin.

Ces exécutions parallèles peuvent se terminer par des exceptions. Obtient donc myMethodune liste d'exceptions.

Je souhaite transmettre la liste d'exceptions en tant que cause racine, mais la cause racine peut être une seule exception. Bien sûr, je peux créer ma propre exception pour atteindre ce que je veux, mais je veux savoir si Java, Spring ou Spring Batch a quelque chose comme ça hors de la boîte.

gstackoverflow
la source
3
.NET a un AggregateExceptionqui contient une liste d'exceptions. Cette idée devrait également s'appliquer à Java.
usr
1
Voir aussi stackoverflow.com/q/8946661/821436
Reinstate Monica - M. Schröder

Réponses:

49

Je ne suis pas sûr que je le ferais (bien que étant donné le 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. Le JavaDoc ne semble pas dire que c'est uniquement pour la JVM à utiliser dans try-with-resources:

Ajoute l'exception spécifiée aux exceptions qui ont été supprimées afin de dé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 valider son argument.

Notez que lorsqu'une exception provoque une autre exception, la première exception est généralement interceptée, puis la deuxième exception est levée en réponse. En d'autres termes, il existe un lien de causalité entre les deux exceptions. En revanche, il existe des situations où deux exceptions indépendantes peuvent être levées dans des blocs de code frère, en particulier dans le bloc try d'une instruction try-with-resources et le bloc finally généré par le compilateur qui ferme la ressource. Dans ces situations, une seule des exceptions levées peut être propagée. Dans l'instruction try-with-resources, lorsqu'il existe 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. Comme une exception déroule la pile,

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

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

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

TJ Crowder
la source
[...] si une exception supprimera ou non d'autres exceptions n'est généralement [...] déterminé qu'après le déclenchement d'une exception. J'imagine que ce ne sera pas le cas lorsque plusieurs exceptions supprimées sont collectées à partir d'exécutions parallèles.
GOTO 0
24

Les exceptions et leurs causes ne sont toujours qu'une chose 1: 1: vous pouvez lever une exception et chaque exception ne peut avoir qu'une seule cause (qui peut à nouveau avoir une cause ...).

Cela pourrait être considéré comme une erreur de conception, en particulier lorsque vous envisagez un comportement multithread comme vous l'avez décrit.

C'est l'une des raisons pour lesquelles Java 7 a ajouté addSuppressedà throwable qui peut essentiellement attacher une quantité arbitraire d'exceptions à une autre seule (l'autre motivation principale était d'essayer avec des ressources qui avait besoin d'un moyen de gérer les exceptions dans le bloc enfin sans abandonner silencieusement leur).

Donc, fondamentalement, lorsque vous avez 1 exception qui entraîne l'échec de votre processus, vous ajoutez celle-ci comme cause de votre exception de niveau supérieur, et si vous en avez plus, vous les ajoutez à celle d'origine en utilisant addSuppressed. L'idée est que cette première exception a "supprimé" les autres devenant membre de la "chaîne d'exceptions réelle".

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);
}
Joachim Sauer
la source
4
Je ne le ferais pas tout à fait comme vous le suggérez, à moins que l'une des exceptions sous-jacentes puisse vraiment être distinguée comme étant 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 la principale - même si l'exception «principale» est une exception TypoInUserInputException et l'une des celles supprimées est une DatabaseCorruptedException.
Ilmari Karonen
1
… Au lieu de cela, je marquerais toutes les exceptions sous-jacentes comme supprimées par l'exception SomethingWentWrongException, et donnerais à cette exception un message indiquant clairement qu'une ou plusieurs exceptions supprimées devraient suivre, par exemple quelque chose comme « X sur Y a échoué, voir la liste des échecs ci-dessous ".
Ilmari Karonen