diff options
author | Carl Mastrangelo <notcarl@google.com> | 2018-08-17 11:40:11 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-08-17 11:40:11 -0700 |
commit | 7fe49f9b520f3106cc147aaab644fff7b79df4b3 (patch) | |
tree | 7eb7d172ee16654ceda8f4ee153ae817158f7e8f /core | |
parent | 30a4bfb2f0d1daf38d3b59e633f300b8edba9faf (diff) | |
download | grpc-grpc-java-7fe49f9b520f3106cc147aaab644fff7b79df4b3.tar.gz |
core: add ability to create stackless status exceptions
Diffstat (limited to 'core')
4 files changed, 170 insertions, 0 deletions
diff --git a/core/src/main/java/io/grpc/StatusException.java b/core/src/main/java/io/grpc/StatusException.java index f9416bf72..e89ac16dc 100644 --- a/core/src/main/java/io/grpc/StatusException.java +++ b/core/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()}. @@ -44,9 +45,25 @@ public class StatusException extends Exception { * @since 1.0.0 */ public StatusException(Status status, @Nullable Metadata trailers) { + this(status, trailers, /*fillInStackTrace=*/ true); + } + + StatusException(Status status, @Nullable Metadata trailers, boolean 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/core/src/main/java/io/grpc/StatusRuntimeException.java b/core/src/main/java/io/grpc/StatusRuntimeException.java index d685464d4..27e2e6117 100644 --- a/core/src/main/java/io/grpc/StatusRuntimeException.java +++ b/core/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#asException()}. * @@ -45,9 +47,25 @@ public class StatusRuntimeException extends RuntimeException { * @since 1.0.0 */ public StatusRuntimeException(Status status, @Nullable Metadata trailers) { + this(status, trailers, /*fillInStackTrace=*/ true); + } + + StatusRuntimeException(Status status, @Nullable Metadata trailers, boolean 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/core/src/test/java/io/grpc/StatusExceptionTest.java b/core/src/test/java/io/grpc/StatusExceptionTest.java new file mode 100644 index 000000000..dd0d12dcc --- /dev/null +++ b/core/src/test/java/io/grpc/StatusExceptionTest.java @@ -0,0 +1,67 @@ +/* + * Copyright 2018 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Tests for {@link StatusException}. + */ +@RunWith(JUnit4.class) +public class StatusExceptionTest { + + @Test + public void internalCtorRemovesStack() { + StackTraceElement[] trace = + new StatusException(Status.CANCELLED, null, false) {}.getStackTrace(); + + assertThat(trace).isEmpty(); + } + + @Test + public void normalCtorKeepsStack() { + StackTraceElement[] trace = + new StatusException(Status.CANCELLED, null) {}.getStackTrace(); + + assertThat(trace).isNotEmpty(); + } + + @Test + public void extendPreservesStack() { + StackTraceElement[] trace = new StatusException(Status.CANCELLED) {}.getStackTrace(); + + assertThat(trace).isNotEmpty(); + } + + @Test + public void extendAndOverridePreservesStack() { + final StackTraceElement element = new StackTraceElement("a", "b", "c", 4); + StatusException exception = new StatusException(Status.CANCELLED, new Metadata()) { + + @Override + public synchronized Throwable fillInStackTrace() { + setStackTrace(new StackTraceElement[]{element}); + return this; + } + }; + assertThat(exception.getStackTrace()).asList().containsExactly(element); + } +} diff --git a/core/src/test/java/io/grpc/StatusRuntimeExceptionTest.java b/core/src/test/java/io/grpc/StatusRuntimeExceptionTest.java new file mode 100644 index 000000000..2c3bf7e9c --- /dev/null +++ b/core/src/test/java/io/grpc/StatusRuntimeExceptionTest.java @@ -0,0 +1,68 @@ +/* + * Copyright 2018 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Tests for {@link StatusRuntimeException}. + */ +@RunWith(JUnit4.class) +public class StatusRuntimeExceptionTest { + + @Test + public void internalCtorRemovesStack() { + StackTraceElement[] trace = + new StatusRuntimeException(Status.CANCELLED, null, false) {}.getStackTrace(); + + assertThat(trace).isEmpty(); + } + + @Test + public void normalCtorKeepsStack() { + StackTraceElement[] trace = + new StatusRuntimeException(Status.CANCELLED, null) {}.getStackTrace(); + + assertThat(trace).isNotEmpty(); + } + + @Test + public void extendPreservesStack() { + StackTraceElement[] trace = new StatusRuntimeException(Status.CANCELLED) {}.getStackTrace(); + + assertThat(trace).isNotEmpty(); + } + + @Test + public void extendAndOverridePreservesStack() { + final StackTraceElement element = new StackTraceElement("a", "b", "c", 4); + StatusRuntimeException exception = + new StatusRuntimeException(Status.CANCELLED, new Metadata()) { + + @Override + public synchronized Throwable fillInStackTrace() { + setStackTrace(new StackTraceElement[]{element}); + return this; + } + }; + assertThat(exception.getStackTrace()).asList().containsExactly(element); + } +} |