¿Cómo puedo pasar una colección de excepciones como causa raíz?

52

Algún método, myMethod invoca varias ejecuciones paralelas y espera sus terminaciones.

Estas ejecuciones paralelas pueden terminar con excepciones. Entonces myMethodobtiene una lista de excepciones.

Quiero pasar la lista de excepciones como causa raíz, pero la causa raíz podría ser solo una excepción. Claro que puedo crear mi propia excepción para lograr lo que quiero, pero quiero saber si Java, Spring o Spring Batch tienen algo como esto fuera de la caja.

gstackoverflow
fuente
3
.NET tiene un AggregateExceptionque contiene una lista de excepciones. Esa idea también debería ser aplicable a Java.
Usr
1
Ver también stackoverflow.com/q/8946661/821436
Restablecer Monica - M. Schröder

Respuestas:

49

No estoy seguro de hacerlo (aunque dado el JavaDoc no podría decirte por qué dudo), pero hay una lista de excepciones suprimidas Throwable, que puedes agregar a través de addSuppressed. JavaDoc no parece decir que esto sea solo para que la JVM lo use en try-with-resources:

Agrega la excepción especificada a las excepciones que se suprimieron para entregar esta excepción. Este método es seguro para subprocesos y, por lo general, la instrucción try-with-resources lo llama (de forma automática e implícita).

El comportamiento de supresión está habilitado a menos que esté deshabilitado a través de un constructor. Cuando la supresión está desactivada, este método no hace nada más que validar su argumento.

Tenga en cuenta que cuando una excepción causa otra excepción, la primera excepción generalmente se detecta y luego se lanza la segunda excepción en respuesta. En otras palabras, hay una conexión causal entre las dos excepciones. En contraste, hay situaciones en las que se pueden generar dos excepciones independientes en bloques de código hermano, en particular en el bloque try de una declaración try-with-resources y el bloque finalmente generado por el compilador que cierra el recurso. En estas situaciones, solo se puede propagar una de las excepciones lanzadas. En la instrucción try-with-resources, cuando hay dos de esas excepciones, la excepción que se origina en el bloque try se propaga y la excepción del último bloque se agrega a la lista de excepciones suprimidas por la excepción del bloque try. Como excepción, se desenrolla la pila,

Una excepción puede haber suprimido las excepciones y al mismo tiempo ser causada por otra excepción. Se sabe semánticamente si una excepción tiene una causa o no en el momento de su creación, a diferencia de si una excepción suprimirá o no otras excepciones que generalmente solo se determinan después de que se lanza una excepción.

Tenga en cuenta que el código escrito del programador también puede aprovechar llamar a este método en situaciones en las que hay varias excepciones entre hermanos y solo se puede propagar una.

Tenga en cuenta el último párrafo, que parece adaptarse a su caso.

TJ Crowder
fuente
[...] si una excepción suprimirá o no otras excepciones [...] generalmente solo se determina después de que se lanza una excepción. Me imagino que este no será el caso cuando se recopilen varias excepciones suprimidas de ejecuciones paralelas.
GOTO 0
24

Las excepciones y sus causas son siempre solo una cosa 1: 1: puede lanzar una excepción y cada excepción solo puede tener una causa (que puede tener una causa ...).

Eso podría considerarse una falla de diseño, especialmente cuando se considera un comportamiento de subprocesos múltiples como lo describió.

Esa es una de las razones por las que Java 7 agregó addSuppressed a tirar, que básicamente puede adjuntar una cantidad arbitraria de excepciones a una sola (la otra motivación principal fue probar con recursos que necesitaban una forma de manejar las excepciones en el bloque finalmente sin caer silenciosamente ellos).

Entonces, básicamente, cuando tiene 1 excepción que hace que su proceso falle, agrega esa como la causa de su excepción de nivel superior, y si tiene más, entonces las agrega a la original usando addSuppressed. La idea es que esa primera excepción "suprimió" a los demás y se convirtieron en miembros de la "cadena de excepción real".

Código de muestra:

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
fuente
44
No lo haría de la manera que usted sugiere, a menos que una de las excepciones subyacentes se pueda identificar como la "principal". Si solo elige arbitrariamente una de las excepciones como la principal y las otras como suprimidas, está invitando a una persona que llama a ignorar las excepciones suprimidas y solo informar la principal, incluso si la excepción "principal" es TypoInUserInputException y una de los suprimidos son una excepción DatabaseCorruptedException.
Ilmari Karonen
1
... En cambio, marcaría todas las excepciones subyacentes como suprimidas por SomethingWentWrongException, y le daría a esa excepción un mensaje que indica claramente que deben seguir una o más excepciones suprimidas , por ejemplo, algo como " X de las tareas Y fallaron, vea la lista de fallas a continuación ".
Ilmari Karonen