Ho avuto difficoltà con il problema di usabilità di SwingWorker che mangiava qualsiasi eccezione generata nell'attività in background, ad esempio, descritta on this SO thread. Quel thread fornisce una buona descrizione del problema, ma non discute il recupero dell'eccezione originale.Eccezioni di SwingWorker perse anche quando si utilizzano le classi wrapper
L'applet che ho ricevuto deve propagare l'eccezione verso l'alto. Ma non sono stato in grado nemmeno di prenderlo. Sto utilizzando la classe di wrapper SimpleSwingWorker da this blog entry specificatamente per provare a risolvere questo problema. È una classe abbastanza piccola ma la ripropongo qui alla fine solo per riferimento.
Il codice chiamante appare ampiamente come
try {
// lots of code here to prepare data, finishing with
SpecialDataHelper helper = new SpecialDataHelper(...stuff...);
helper.execute(); // this will call get+done on the actual worker
} catch (Throwable e) {
// used "Throwable" here in desperation to try and get
// anything at all to match, including unchecked exceptions
//
// no luck, this code is never ever used :-(
}
Gli involucri:
class SpecialDataHelper extends SimpleSwingWorker {
public SpecialDataHelper (SpecialData sd) {
this.stuff = etc etc etc;
}
public Void doInBackground() throws Exception {
OurCodeThatThrowsACheckedException(this.stuff);
return null;
}
protected void done() {
// called only when successful
// never reached if there's an error
}
}
La caratteristica di SimpleSwingWorker
è che done()/get()
metodi del SwingWorker reale sono chiamati automaticamente. Questo, in teoria, ripaga tutte le eccezioni avvenute in background. In pratica, nulla viene mai catturato e non so nemmeno perché.
La classe SimpleSwingWorker, per riferimento, e con niente eliso per brevità:
import java.util.concurrent.ExecutionException;
import javax.swing.SwingWorker;
/**
* A drop-in replacement for SwingWorker<Void,Void> but will not silently
* swallow exceptions during background execution.
*
* Taken from http://jonathangiles.net/blog/?p=341 with thanks.
*/
public abstract class SimpleSwingWorker {
private final SwingWorker<Void,Void> worker =
new SwingWorker<Void,Void>() {
@Override
protected Void doInBackground() throws Exception {
SimpleSwingWorker.this.doInBackground();
return null;
}
@Override
protected void done() {
// Exceptions are lost unless get() is called on the
// originating thread. We do so here.
try {
get();
} catch (final InterruptedException ex) {
throw new RuntimeException(ex);
} catch (final ExecutionException ex) {
throw new RuntimeException(ex.getCause());
}
SimpleSwingWorker.this.done();
}
};
public SimpleSwingWorker() {}
protected abstract Void doInBackground() throws Exception;
protected abstract void done();
public void execute() {
worker.execute();
}
}
Tutto questo si basa su una falsa ipotesi. Leggi il [javadoc per il metodo get()] (http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html#get%28%29): genera una ExecutionException se il calcolo dello sfondo ha generato un'eccezione. –
Vedere anche questo [Q & A] (http://stackoverflow.com/q/7053865/230513). – trashgod
@ JBNizet Sì, è per questo che le chiamate done() di SimpleSwingWorker get(), intercettano ExecutionException e la riflettono come nuova RuntimeException. Non è questo il punto? Se no, allora stiamo parlando l'un l'altro e dovrai essere più esplicito. –