diff --git a/api/src/main/java/io/grpc/StatusException.java b/api/src/main/java/io/grpc/StatusException.java index b719f881132..e89ac16dc6c 100644 --- a/api/src/main/java/io/grpc/StatusException.java +++ b/api/src/main/java/io/grpc/StatusException.java @@ -27,6 +27,7 @@ public class StatusException extends Exception { private static final long serialVersionUID = -660954903976144640L; private final Status status; private final Metadata trailers; + private final boolean fillInStackTrace; /** * Constructs an exception with both a status. See also {@link Status#asException()}. @@ -48,10 +49,21 @@ public StatusException(Status status, @Nullable Metadata trailers) { } StatusException(Status status, @Nullable Metadata trailers, boolean fillInStackTrace) { - super(Status.formatThrowableMessage(status), status.getCause(), - /* enableSuppression */ true, /* writableStackTrace */fillInStackTrace); + super(Status.formatThrowableMessage(status), status.getCause()); this.status = status; this.trailers = trailers; + this.fillInStackTrace = fillInStackTrace; + fillInStackTrace(); + } + + @Override + public synchronized Throwable fillInStackTrace() { + // Let's observe final variables in two states! This works because Throwable will invoke this + // method before fillInStackTrace is set, thus doing nothing. After the constructor has set + // fillInStackTrace, this method will properly fill it in. Additionally, sub classes may call + // this normally, because fillInStackTrace will either be set, or this method will be + // overriden. + return fillInStackTrace ? super.fillInStackTrace() : this; } /** diff --git a/api/src/main/java/io/grpc/StatusRuntimeException.java b/api/src/main/java/io/grpc/StatusRuntimeException.java index 70c4d10f0b2..68b816fc7fa 100644 --- a/api/src/main/java/io/grpc/StatusRuntimeException.java +++ b/api/src/main/java/io/grpc/StatusRuntimeException.java @@ -29,6 +29,8 @@ public class StatusRuntimeException extends RuntimeException { private final Status status; private final Metadata trailers; + private final boolean fillInStackTrace; + /** * Constructs the exception with both a status. See also {@link Status#asRuntimeException()}. * @@ -49,10 +51,21 @@ public StatusRuntimeException(Status status, @Nullable Metadata trailers) { } StatusRuntimeException(Status status, @Nullable Metadata trailers, boolean fillInStackTrace) { - super(Status.formatThrowableMessage(status), status.getCause(), - /* enable suppressions */ true, /* writableStackTrace */ fillInStackTrace); + super(Status.formatThrowableMessage(status), status.getCause()); this.status = status; this.trailers = trailers; + this.fillInStackTrace = fillInStackTrace; + fillInStackTrace(); + } + + @Override + public synchronized Throwable fillInStackTrace() { + // Let's observe final variables in two states! This works because Throwable will invoke this + // method before fillInStackTrace is set, thus doing nothing. After the constructor has set + // fillInStackTrace, this method will properly fill it in. Additionally, sub classes may call + // this normally, because fillInStackTrace will either be set, or this method will be + // overriden. + return fillInStackTrace ? super.fillInStackTrace() : this; } /**