diff --git a/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java b/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java index 94d46e8c662..32545155b53 100644 --- a/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java +++ b/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java @@ -306,13 +306,57 @@ public class CompletableFuture implements Future, CompletionStage { return RESULT.compareAndSet(this, null, (t == null) ? NIL : t); } + static CompletionException wrapInCompletionException(Throwable t) { + if (t == null) + return new CompletionException(); + + String message; + Throwable suppressed; + try { + message = t.toString(); + suppressed = null; + } catch (Throwable unknown) { + message = ""; + suppressed = unknown; + } + + final CompletionException wrapping = new CompletionException(message, t); + + if (suppressed != null) + wrapping.addSuppressed(suppressed); + + return wrapping; + } + + static ExecutionException wrapInExecutionException(Throwable t) { + if (t == null) + return new ExecutionException(); + + String message; + Throwable suppressed; + try { + message = t.toString(); + suppressed = null; + } catch (Throwable unknown) { + message = ""; + suppressed = unknown; + } + + final ExecutionException wrapping = new ExecutionException(message, t); + + if (suppressed != null) + wrapping.addSuppressed(suppressed); + + return wrapping; + } + /** * Returns the encoding of the given (non-null) exception as a * wrapped CompletionException unless it is one already. */ static AltResult encodeThrowable(Throwable x) { return new AltResult((x instanceof CompletionException) ? x : - new CompletionException(x)); + wrapInCompletionException(x)); } /** Completes with an exceptional result, unless already completed. */ @@ -329,7 +373,7 @@ public class CompletableFuture implements Future, CompletionStage { */ static Object encodeThrowable(Throwable x, Object r) { if (!(x instanceof CompletionException)) - x = new CompletionException(x); + x = wrapInCompletionException(x); else if (r instanceof AltResult && x == ((AltResult)r).ex) return r; return new AltResult(x); @@ -365,7 +409,7 @@ public class CompletableFuture implements Future, CompletionStage { if (r instanceof AltResult && (x = ((AltResult)r).ex) != null && !(x instanceof CompletionException)) - r = new AltResult(new CompletionException(x)); + r = new AltResult(wrapInCompletionException(x)); return r; } @@ -393,7 +437,7 @@ public class CompletableFuture implements Future, CompletionStage { if ((x instanceof CompletionException) && (cause = x.getCause()) != null) x = cause; - throw new ExecutionException(x); + throw wrapInExecutionException(x); } return r; } @@ -410,7 +454,7 @@ public class CompletableFuture implements Future, CompletionStage { throw new CancellationException(details, (CancellationException)x); if (x instanceof CompletionException) throw (CompletionException)x; - throw new CompletionException(x); + throw wrapInCompletionException(x); } return r; } @@ -2605,8 +2649,8 @@ public class CompletableFuture implements Future, CompletionStage { /** * Returns a string identifying this CompletableFuture, as well as * its completion state. The state, in brackets, contains the - * String {@code "Completed Normally"} or the String {@code - * "Completed Exceptionally"}, or the String {@code "Not + * String {@code "Completed normally"} or the String {@code + * "Completed exceptionally"}, or the String {@code "Not * completed"} followed by the number of CompletableFutures * dependent upon its completion, if any. * @@ -2623,7 +2667,7 @@ public class CompletableFuture implements Future, CompletionStage { ? "[Not completed]" : "[Not completed, " + count + " dependents]") : (((r instanceof AltResult) && ((AltResult)r).ex != null) - ? "[Completed exceptionally: " + ((AltResult)r).ex + "]" + ? "[Completed exceptionally: " + ((AltResult)r).ex.getClass().getName() + "]" : "[Completed normally]")); } diff --git a/test/jdk/java/util/concurrent/tck/CompletableFutureTest.java b/test/jdk/java/util/concurrent/tck/CompletableFutureTest.java index 8b7d69b1ef4..fa6c4f3c885 100644 --- a/test/jdk/java/util/concurrent/tck/CompletableFutureTest.java +++ b/test/jdk/java/util/concurrent/tck/CompletableFutureTest.java @@ -80,7 +80,14 @@ public class CompletableFutureTest extends JSR166TestCase { return new TestSuite(CompletableFutureTest.class); } - static class CFException extends RuntimeException {} + static class CFException extends RuntimeException { + // This makes sure that CompletableFuture still behaves appropriately + // even if thrown exceptions end up throwing exceptions from their String + // representations. + @Override public String getMessage() { + throw new IllegalStateException("malformed"); + } + } void checkIncomplete(CompletableFuture f) { assertFalse(f.isDone()); @@ -272,8 +279,8 @@ public class CompletableFutureTest extends JSR166TestCase { */ public void testCompleteExceptionally() { CompletableFuture f = new CompletableFuture<>(); - CFException ex = new CFException(); checkIncomplete(f); + CFException ex = new CFException(); f.completeExceptionally(ex); checkCompletedExceptionally(f, ex); } @@ -5142,5 +5149,4 @@ public class CompletableFutureTest extends JSR166TestCase { checkCompletedWithWrappedException(g.toCompletableFuture(), r.ex); r.assertInvoked(); }} - }