Reputation: 80
So, I am having an issue where Sonar Qube is flagging my code due to improper handling of InterruptedExceptions
. The exact Sonar Qube error is java:S2142
, and can be found here: https://github.com/joansmith/sonar-java/blob/master/java-checks/src/main/resources/org/sonar/l10n/java/rules/squid/S2142.html
My code is below - Sonar Qube is flagging the handling of possible InterruptedExceptions
in extractFutureIntoList()
as the source of the problem:
@Service
@Log4j2
public class AsynchronousDataGrabber {
ExecutorService executorService = Executors.newFixedThreadPool(10);
public List<MyDataObject> getDataAsynchronously() {
Optional<Future<MyDataObject>> future01 = getDataFuture("01");
Optional<Future<MyDataObject>> future02 = getDataFuture("02");
Optional<Future<MyDataObject>> future03 = getDataFuture("03");
List<MyDataObject> list = new ArrayList();
extractFutureIntoList(future01, list);
extractFutureIntoList(future02, list);
extractFutureIntoList(future03, list);
return list;
}
private Optional<Future<MyDataObject>> getDataFuture(String key) {
try {
return Optional.of(executorService.submit(() -> getDataFromRemoteApi(key)));
} catch(Exception e) {
log.error("Exception", e);
return Optional.empty();
}
}
private void extractFutureIntoList(Optional<Future<MyDataObject>> future, List<MyDataObject> list) {
try {
if (future.isPresent()) {
// This is apparently where the `InterruptedException` can occur - Future.get()
list.add(future.get().get());
}
} catch (Exception e) {
// SonarQube is telling me that merely logging an `InterupptedException` is not enough
// Apparently, I must either rethrow the `InterruptedException`,
//or call `Thread.currentThread().interrupt()
log.error("Exception", e);
return;
}
}
}
Sonar Qube proposes that I solve this problem either by rethrowing InterruptedException
, or by calling Thread.currentThread().interrupt()
-- whenever Future.get()
is interrupted.
My problem with this is that getDataAsynchronously()
is being called by my application's main thread. If the only acceptable response to an InterruptedException
is to rethrow it or interrupt the current thread, then a single interrupt in one of my executorService's
threads will bring down my entire application. This seems to be an excessive response, especially given that the task being run by the threads - getDataFromRemoteApi()
- may not always be successful anyway.
Is there a better way to handle InterruptedException
- ideally one that is acceptable to Sonar Qube, but doesn't involve killing the thread that is trying to call Future.get()
?
I have tried logging the InterruptedException
(log.error("Exception", e);
), and I have tried to catch and rethrow the InterruptedException
(throw new RuntimeException(e);
) - neither appeased Sonar Qube.
Upvotes: 3
Views: 1500
Reputation: 2052
You should never ignore InterruptedException
indeed.
Risen InterruptedException
means running thread was marked as interrupted, that signal (could be checked with Thread.currentThread.isInterrupted()
) was received and removed. So catching it without throwing up/calling Thread.currentThread().interrupt()
leads to forgetting the fact of interruption forever.
If you have caught an InterruptedException
do graceful return from the method and throw exception up or recover the flag and return.
In your particular case getting an InterruptedException
in the specified line means your main thread has been interrupted (not an executor's). It means somebody stopped the program, thus is not interested in method return value anymore. I suggest you either:
Thread.currentThread().interrupt()
and then throw new RuntimeException(e)
. That's okey - you both keep the flag of interruption and indicate that your getDataAsynchronously()
hasn't been executed correctly and there is no answer to return.Thread.currentThread().interrupt()
and just return from extractFutureIntoList
. Your program would continue executing extractFutureIntoList
calls left. If any of other futures would be already complete, thus their result is available immediately, future.get()
would return calculation result without throwing InterruptedException
. So you would collect data from all futures done at the moment of thread interruption. It would be a kind of graceful shutdown.getDataAsynchronously
as throws InterruptedException
and re-throw exception. It's a best practice - mark any blocking method as throws InterruptedException
. Your method getDataAsynchronously
is blocking and actually implies to throw it.Upvotes: 4
Reputation: 16238
The catching of an InterruptedException
also clears the interrupted status of the thread. If you decide to eat the InterruptedException
, then in all cases I can think of you should call Thread.currentThread().interrupt()
to re-mark the thread as interrupted, so that any other call on that thread to Thread.isInterrupted()
will report that an interrupt occurred. The other viable alternative (which SonarQube suggests) is to change various method signatures to declare that InterruptedException
can be thrown, and then don't catch the exception. Both of these approaches ensure that the fact that the thread was interrupted at some point is available to interested parties.
In almost all cases I can think of where you can't adjust method signatures, the first option is exactly what you want to do.
It sounds like for whatever (perhaps valid) reason maybe you don't want to do this, so assuming that your case is valid, I suppose you could just (as SonarQube suggests) simply log the exception without resetting the original interrupted status of the thread, but just be aware that you're losing information in this case (everything will look to other parties as if an interrupt never occurred).
Upvotes: 2