I have a method that performs some task with a timeout. I use the ExecutorServer.submit() to get a Future object, and then I call future.get() with a timeout. This is workin
The javadoc of java.util.concurrent.Future.get()
states the following. Then why not just catch ExecutionException (and Cancellation and Interrupted as declared by the java.util.concurrent.Future.get()
) method?
...
Throws:CancellationException - if the computation was cancelled
ExecutionException - if the computation threw an exception
InterruptedException - if the current thread was interrupted while waiting
So basically you can throw whatever exception within your callable and just catch ExecutionException
. Then ExecutionException.getCause()
will hold the actual exception your callable threw as stated in the javadoc. This way you are shielded from method signature changes related to checked exception declaration.
By the way you should never catch Throwable
, as this would catch also RuntimeExceptions
and Errors
. Catching Exception
is a little bit better but still not recommended, as it will catch RuntimeExceptions
.
Something like:
try {
MyResult result = myFutureTask.get();
} catch (ExecutionException e) {
if (errorHandler != null) {
errorHandler.handleExecutionException(e);
}
logger.error(e);
} catch (CancellationException e) {
if (errorHandler != null) {
errorHandler.handleCancelationException(e);
}
logger.error(e);
} catch (InterruptedException e) {
if (errorHandler != null) {
errorHandler.handleInterruptedException(e);
}
logger.error(e);
}
Here is another way to do it, though I'm not convinced that this is less clumsy or less prone to break than to do it with an instanceof check as in your question:
public static byte[] doSomethingWithTimeout(int timeout)
throws ProcessExecutionException, InterruptedException, IOException, TimeoutException {
....
try {
....
return future.get(1000, TimeUnit.MILLISECONDS);
.....
} catch (ExecutionException e) {
try {
throw e.getCause();
} catch (IOException ioe) {
throw ioe;
} catch (InterruptedException ie) {
throw ie;
} catch (ProcessExecutionException pee) {
throw pee;
} catch (Throwable t) {
//Unhandled exception from Callable endups here
}
} catch (TimeoutException e) {
throw e;
} catch.....
}
Here are couple of interesting information for checked and against Checked Exceptions. Brian Goetz discussion and an argument of against checked exceptions from Eckel Discussion. But I did not know if you have already implemented and given a thought about the checked exception refactor that is discussed by Joshua in this book.
According the Effective Java pearls, one of the preferred method of handling Checked exceptions is to turn a checked exception into an Un-Checked Exception. So for example,
try{
obj.someAction()
}catch(CheckedException excep){
}
change this implementation to
if(obj.canThisOperationBeperformed){
obj.someAction()
}else{
// Handle the required Exception.
}
I've found one way to solve the issue. If it's ExecutionException you can get original one by calling exception.getCause() Then you need to wrap your exception in some kind of Runtime Exception or (what is the best way for me) use @SneakyThrows annotation from project lombok (https://projectlombok.org/). I give a small piece of code example. In addition you can add a few instanceof checks before throwing an exception to be sure this is the one you're expecting.
@SneakyThrows
public <T> T submitAndGet(Callable<T> task) {
try {
return executor.submit(task).get(5, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
throw e.getCause();
}
}
Here goes my answer. Let's suppose this code
public class Test {
public static class Task implements Callable<Void>{
@Override
public Void call() throws Exception {
throw new IOException();
}
}
public static class TaskExecutor {
private ExecutorService executor;
public TaskExecutor() {
this.executor = Executors.newSingleThreadExecutor();
}
public void executeTask(Task task) throws IOException, Throwable {
try {
this.executor.submit(task).get();
} catch (ExecutionException e) {
throw e.getCause();
}
}
}
public static void main(String[] args) {
try {
new TaskExecutor().executeTask(new Task());
} catch (IOException e) {
System.out.println("IOException");
} catch (Throwable e) {
System.out.println("Throwable");
}
}
}
IOException will be printed. I think it is an acceptable solution with the downside of throwing and catching Throwable forcefully and that the final catch can be reduced to
} catch (Throwable e) { ... }
Also, another chance is doing it in the following way
public class Test {
public static class Task implements Callable<Void>{
private Future<Void> myFuture;
public void execute(ExecutorService executorService) {
this.myFuture = executorService.submit(this);
}
public void get() throws IOException, InterruptedException, Throwable {
if (this.myFuture != null) {
try {
this.myFuture.get();
} catch (InterruptedException e) {
throw e;
} catch (ExecutionException e) {
throw e.getCause();
}
} else {
throw new IllegalStateException("The task hasn't been executed yet");
}
}
@Override
public Void call() throws Exception {
throw new IOException();
}
}
public static void main(String[] args) {
try {
Task task = new Task();
task.execute(Executors.newSingleThreadExecutor());
task.get();
} catch (IOException e) {
System.out.println("IOException");
} catch (Throwable e) {
System.out.println("Throwable");
}
}
}