Handling the cause of an ExecutionException
Asked Answered
J

3

7

Suppose I have a class defining a big block of work to be done, that can produce several checked Exceptions.

class WorkerClass{
   public Output work(Input input) throws InvalidInputException, MiscalculationException {
      ...
   }
}

Now suppose I have a GUI of some sort that can call this class. I use a SwingWorker to delegate the task.

Final Input input = getInput();
SwingWorker<Output, Void> worker = new SwingWorker<Output, Void>() {
        @Override
        protected Output doInBackground() throws Exception {
            return new WorkerClass().work(input);
        }
};

How can I handle the possible exceptions thrown from the SwingWorker? I want to differentiate between the Exceptions of my worker class (InvalidInputException and MiscalculationException), but the ExecutionException wrapper complicates things. I only want to handle these Exceptions - an OutOfMemoryError should not be caught.

try{
   worker.execute();
   worker.get();
} catch(InterruptedException e){
   //Not relevant
} catch(ExecutionException e){
   try{
      throw e.getCause(); //is a Throwable!
   } catch(InvalidInputException e){
      //error handling 1
   } catch(MiscalculationException e){
      //error handling 2
   }
}
//Problem: Since a Throwable is thrown, the compiler demands a corresponding catch clause.
Jonell answered 6/2, 2013 at 13:6 Comment(1)
possible duplicate of What is the best way to handle an ExecutionException?Kernan
C
8
catch (ExecutionException e) {
    Throwable ee = e.getCause ();

    if (ee instanceof InvalidInputException)
    {
        //error handling 1
    } else if (ee instanceof MiscalculationException e)
    {
        //error handling 2
    }
    else throw e; // Not ee here
}
Conal answered 6/2, 2013 at 13:13 Comment(2)
Cleanest answer in my oppinion.Jonell
In some cases, instead of throw e you many want to do throw new RuntimeException (ee)Conal
H
1

You could use an ugly (smart?) hack to convert the throwable into an unchecked exception. The advantage is that the calling code will receive whatever exception was thrown by your worker thread, whether checked or unchecked, but you don't have to change the signature of your method.

try {
    future.get();
} catch (InterruptedException ex) {
} catch (ExecutionException ex) {
    if (ex.getCause() instanceof InvalidInputException) {
        //do your stuff
    } else {
        UncheckedThrower.throwUnchecked(ex.getCause());
    }
}

With UncheckedThrower defined as:

class UncheckedThrower {

    public static <R> R throwUnchecked(Throwable t) {
        return UncheckedThrower.<RuntimeException, R>trhow0(t);
    }

    @SuppressWarnings("unchecked")
    private static <E extends Throwable, R> R trhow0(Throwable t) throws E {
        throw (E) t;
    }
}
Havstad answered 6/2, 2013 at 13:15 Comment(3)
I had my doubts whether this method would work if an unexpected Exception (or Error) would be thrown. I would have expected an additional ClassCastException, but it does not occur. This method works like a charm, but I'm not sure why it does :D Do you have any reference explaining why it does? +1 because I learned something new.Jonell
@Jonell Generics magic - not sure how it works in details to be honest. Probably some obscure rule of the language!Havstad
The link you provided gives a hint... the java byte code version of this code does not do an actual check for type. I suspect it has to do with the Exception hierarchy being somewhat "strange", where RuntimeException is a subclass of Exception, but does not require to be declared, where a normal Exception does.Jonell
H
0

Try/multi-catch:

try {
    worker.execute();
    worker.get();
} catch (InterruptedException e) {
    //Not relevant
} catch (InvalidInputException e) {
    //stuff
} catch (MiscalculationException e) {
    //stuff
}

Or with the ExecutionException wrapper:

catch (ExecutionException e) {
    e = e.getCause();
    if (e.getClass() == InvalidInputException.class) {
        //stuff
    } else if (e.getClass() == MiscalculationException.class) {
        //stuff
    }
}

Or if you want exceptions' subclasses to be treated like their parents:

catch (ExecutionException e) {
    e = e.getCause();
    if (e instanceof InvalidInputException) {
        //stuff
    } else if (e instanceof MiscalculationException) {
        //stuff
    }
}
Humanity answered 6/2, 2013 at 13:8 Comment(3)
This wont work, since InvalidInputException and MiscalculationException are never thrown at that point. Only an ExectuionException containing those can be occur.Jonell
(I did not downvote btw.) I always heard instanceof should be avoided most of the time - but it might be only way indeed... Why did you change your answer from instanceof to a getClass() call? Instanceof seems better suited, since there might be subclasses of the exceptions...Jonell
@Jonell If you're fine with subclasses of exceptions being treated the same way as their parent, then...Humanity

© 2022 - 2024 — McMap. All rights reserved.