Come posso passare una raccolta di eccezioni come causa principale?


52

Qualche metodo, myMethodinvoca diverse esecuzioni parallele e attende la loro conclusione.

Queste esecuzioni parallele possono terminare con eccezioni. Quindi myMethodottiene un elenco di eccezioni.

Voglio passare l'elenco delle eccezioni come causa principale, ma la causa principale potrebbe essere solo una singola eccezione. Sicuramente posso creare la mia eccezione per ottenere ciò che voglio, ma voglio sapere se Java, Spring o Spring Batch hanno qualcosa di simile fuori dalla scatola.


3
.NET ha un AggregateExceptionche contiene un elenco di eccezioni. Questa idea dovrebbe essere applicabile anche a Java.
usr

Risposte:


49

Non sono sicuro che lo farei (anche se dato JavaDoc non potrei dirti perché esito), ma c'è l'elenco delle eccezioni soppresse su Throwablecui puoi aggiungere tramite addSuppressed. JavaDoc non sembra affermare che questo sia solo per JVM da utilizzare nelle risorse di prova:

Aggiunge l'eccezione specificata alle eccezioni che sono state eliminate per consegnare questa eccezione. Questo metodo è thread-safe e in genere chiamato (automaticamente e implicitamente) dall'istruzione try-with-resources.

Il comportamento di soppressione è abilitato a meno che non sia disabilitato tramite un costruttore. Quando la soppressione è disabilitata, questo metodo non fa altro che convalidare il suo argomento.

Si noti che quando un'eccezione provoca un'altra eccezione, la prima eccezione viene in genere rilevata e quindi la seconda eccezione viene generata in risposta. In altre parole, esiste una connessione causale tra le due eccezioni. Al contrario, ci sono situazioni in cui è possibile generare due eccezioni indipendenti nei blocchi di codice di pari livello, in particolare nel blocco try di un'istruzione try-with-resources e nel blocco infine generato dal compilatore che chiude la risorsa. In queste situazioni, è possibile propagare solo una delle eccezioni generate. Nell'istruzione try-with-resources, quando esistono due di tali eccezioni, l'eccezione originata dal blocco try viene propagata e l'eccezione dal blocco finally viene aggiunta all'elenco delle eccezioni soppresse dall'eccezione dal blocco try. Come eccezione si svolge lo stack,

Un'eccezione può aver soppresso le eccezioni mentre era causata anche da un'altra eccezione. Il fatto che un'eccezione abbia o meno una causa è semanticamente noto al momento della sua creazione, a differenza del fatto che un'eccezione sopprimerà o meno altre eccezioni che sono in genere determinate solo dopo che viene generata un'eccezione.

Si noti che il codice scritto del programmatore è anche in grado di trarre vantaggio dalla chiamata di questo metodo in situazioni in cui vi sono più eccezioni di pari livello e solo una può essere propagata.

Nota l'ultimo paragrafo, che sembra adattarsi al tuo caso.


[...] se un'eccezione sopprimerà o meno altre eccezioni [...] è in genere determinato solo dopo che viene generata un'eccezione. Immagino che questo non accadrà quando verranno raccolte più eccezioni soppresse da corse parallele.
GOTO 0

24

Eccezioni e le loro cause sono sempre e solo un 1: 1 cosa: si può buttare un eccezione e ogni eccezione può avere solo una causa (che può ancora avere una causa ...).

Questo potrebbe essere considerato un errore di progettazione, specialmente se si considera il comportamento multi-thread come descritto.

Questo è uno dei motivi per cui Java 7 è stato aggiunto addSuppressedal lancio che può fondamentalmente collegare una quantità arbitraria di eccezioni a un altro (l'altra motivazione principale era provare con le risorse che avevano bisogno di un modo per gestire le eccezioni nel blocco finalmente senza far cadere silenziosamente loro).

Quindi fondamentalmente quando hai 1 eccezione che causa il fallimento del tuo processo, aggiungi quella come causa della tua eccezione di livello superiore, e se ne hai altre, aggiungi quelle a quella originale usando addSuppressed. L'idea è che quella prima eccezione "ha soppresso" le altre diventando un membro della "vera catena delle eccezioni".

Codice di esempio:

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
Non lo farei proprio come suggerisci, a meno che una delle eccezioni sottostanti non possa davvero essere individuata come "principale". Se scegli arbitrariamente una delle eccezioni come principale e le altre come soppresse, stai invitando un chiamante a ignorare le eccezioni soppresse e a segnalare solo quella principale, anche se l'eccezione "principale" è una TypoInUserInputException e una di quelli soppressi è DatabaseCorruptedException.
Ilmari Karonen,

1
... Invece, contrassegnerei tutte le eccezioni sottostanti come soppresse da SomethingWentWrongException e darei a quell'eccezione un messaggio che indica chiaramente che dovrebbero seguire una o più eccezioni soppresse , ad esempio qualcosa come "Attività X su Y non riuscite, vedere l'elenco degli errori di seguito ".
Ilmari Karonen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.