After I have switched from Java 11 to Java 17 (OpenJDK installed from Ubuntu 20.04 repository), the following code doesn't work:
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.exception.ExceptionUtils;
public class TestClass {
public static void main(String[] args) {
CompletableFuture<String> streamFuture = CompletableFuture.supplyAsync(() -> {
throw MyException.wrapIfNeeded(new Exception("MyException"));
});
String result = null;
try {
result = streamFuture.get();
} catch (Exception e) {
System.out.println("Exception: " + ExceptionUtils.getMessage(e));
}
System.out.println("Result: " + Objects.toString(result));
}
static class MyException extends RuntimeException {
private static final long serialVersionUID = 3349188601484197015L;
public MyException(Throwable cause) {
super(cause == null ? null : cause.getMessage(), cause);
}
public static MyException wrapIfNeeded(Throwable e) {
return e instanceof MyException ? (MyException) e : new MyException(e);
}
@Override
public String toString() {
return ReflectionToStringBuilder.toString(this);
}
}
}
There is a problem in streamFuture.get()
- it hangs infinitely. I dug deeper and found that in java.util.concurrent.ForkJoinPool
there is a method unmanagedBlock(ManagedBlocker blocker)
which looks like
/** ManagedBlock for external threads */
private static void unmanagedBlock(ManagedBlocker blocker)
throws InterruptedException {
if (blocker == null) throw new NullPointerException();
do {} while (!blocker.isReleasable() && !blocker.block());
}
and the program hangs infinitely in the do-while loop.
EDIT:
I found out the problem is cause by the toString()
method added to my custom exception class. For some reason it started to be a problem after Java 11
Exception Handling of CompletableFuture The call to get() throws an ExecutionException which causes the root Exception.
The CompletableFuture. get() method is blocking. It waits until the Future is completed and returns the result after its completion.
Overview. join() is an instance method of the CompletableFuture class. It is used to return the value when the future is complete or throws an unchecked exception if completed exceptionally.
A CompletableFuture is an extension to Java's Future API which was introduced in Java 8. A Future is used for asynchronous Programming. It provides two methods, isDone() and get(). The methods retrieve the result of the computation when it completes.
ReflectionToStringBuilder.toString
use encapsulated java API.We can try something like MyException.wrapIfNeeded(new NullPointerException("")).toString()
And will see
Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make field static final long java.lang.RuntimeException.serialVersionUID accessible: module java.base does not "opens java.lang" to unnamed module @b4c966a
In CompletableFuture.AsyncSupply#run
method
static final class AsyncSupply<T> extends ForkJoinTask<Void>
implements Runnable, AsynchronousCompletionTask {
...
public void run() {
CompletableFuture<T> d; Supplier<? extends T> f;
if ((d = dep) != null && (f = fn) != null) {
dep = null; fn = null;
if (d.result == null) {
try {
d.completeValue(f.get());
} catch (Throwable ex) {
d.completeThrowable(ex);
}
}
d.postComplete();
}
}
Since MyException
is thrown, d.completeThrowable(ex);
is called, inside this method, it will wrap the exception to CompletionException
, which will call the constructor of Throwable
with MyException
as cause
.
public Throwable(Throwable cause) {
fillInStackTrace();
detailMessage = (cause==null ? null : cause.toString());
this.cause = cause;
}
When cause.toString()
is called, it will throw the exception mentioned in the beginning,
so d.completeThrowable(ex);
is not executed completely and hence the program will hang forever.
I found out the problem is cause by the toString()
method added to my custom exception class. For some reason it (or to be more precise: ReflectionToStringBuilder.toString(this);
) started to be a problem after Java 11.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With