Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion api/src/main/java/io/grpc/InternalStatus.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ private InternalStatus() {}
@Internal
public static final StatusRuntimeException asRuntimeException(Status status,
@Nullable Metadata trailers, boolean fillInStackTrace) {
return new StatusRuntimeException(status, trailers, fillInStackTrace);
return new StatusRuntimeException.Builder()
.setStatus(status)
.setTrailers(trailers)
.setFillInStackTrace(fillInStackTrace)
.build();
}
}
8 changes: 4 additions & 4 deletions api/src/main/java/io/grpc/Status.java
Original file line number Diff line number Diff line change
Expand Up @@ -522,30 +522,30 @@ public boolean isOk() {
* to recover this {@link Status} instance when the returned exception is in the causal chain.
*/
public StatusRuntimeException asRuntimeException() {
return new StatusRuntimeException(this);
return new StatusRuntimeException.Builder().setStatus(this).build();
}

/**
* Same as {@link #asRuntimeException()} but includes the provided trailers in the returned
* exception.
*/
public StatusRuntimeException asRuntimeException(@Nullable Metadata trailers) {
return new StatusRuntimeException(this, trailers);
return new StatusRuntimeException.Builder().setStatus(this).setTrailers(trailers).build();
}

/**
* Convert this {@link Status} to an {@link Exception}. Use {@link #fromThrowable}
* to recover this {@link Status} instance when the returned exception is in the causal chain.
*/
public StatusException asException() {
return new StatusException(this);
return new StatusException.Builder().setStatus(this).build();
}

/**
* Same as {@link #asException()} but includes the provided trailers in the returned exception.
*/
public StatusException asException(@Nullable Metadata trailers) {
return new StatusException(this, trailers);
return new StatusException.Builder().setStatus(this).setTrailers(trailers).build();
}

/** A string representation of the status useful for debugging. */
Expand Down
73 changes: 56 additions & 17 deletions api/src/main/java/io/grpc/StatusException.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,16 @@ public class StatusException extends Exception {
private final boolean fillInStackTrace;

/**
* Constructs an exception with both a status. See also {@link Status#asException()}.
* Constructs an exception with status, trailers, and whether to fill in the stack trace.
* See also {@link Status#asException()} and {@link Status#asException(Metadata)}.
*
* @since 1.0.0
*/
public StatusException(Status status) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no way we can delete this constructor. This is stable API.

this(status, null);
}

/**
* Constructs an exception with both a status and trailers. See also
* {@link Status#asException(Metadata)}.
*
* @since 1.0.0
*/
public StatusException(Status status, @Nullable Metadata trailers) {
this(status, trailers, /*fillInStackTrace=*/ true);
}

StatusException(Status status, @Nullable Metadata trailers, boolean fillInStackTrace) {
protected 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
Expand Down Expand Up @@ -83,4 +69,57 @@ public final Status getStatus() {
public final Metadata getTrailers() {
return trailers;
}

/**
* Builder for creating a {@link StatusException}.
*
* @since 1.62.0
*/
public static class Builder {
private Status status;
private Metadata trailers = null;
private boolean fillInStackTrace = true;

/**
* Sets the status.
*
* @since 1.62.0
*/
public Builder setStatus(final Status status) {
this.status = status;
return this;
}

/**
* Sets the trailers.
*
* @since 1.62.0
*/
public Builder setTrailers(final Metadata trailers) {
this.trailers = trailers;
return this;
}

/**
* Sets whether to fill in the stack trace.
*
* @since 1.62.0
*/
public Builder setFillInStackTrace(final boolean fillInStackTrace) {
this.fillInStackTrace = fillInStackTrace;
return this;
}

/**
* Builds the exception.
*
* @since 1.62.0
*/
public StatusException build() {
final StatusException statusException =
new StatusException(status, trailers, fillInStackTrace);
statusException.fillInStackTrace();
return statusException;
}
}
}
74 changes: 57 additions & 17 deletions api/src/main/java/io/grpc/StatusRuntimeException.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,30 +32,17 @@ public class StatusRuntimeException extends RuntimeException {
private final boolean fillInStackTrace;

/**
* Constructs the exception with both a status. See also {@link Status#asRuntimeException()}.
* Constructs an exception with status, trailers, and whether to fill in the stack trace.
* See also {@link Status#asRuntimeException()} and {@link Status#asRuntimeException(Metadata)}.
*
* @since 1.0.0
*/
public StatusRuntimeException(Status status) {
this(status, null);
}

/**
* Constructs the exception with both a status and trailers. See also {@link
* Status#asRuntimeException(Metadata)}.
*
* @since 1.0.0
*/
public StatusRuntimeException(Status status, @Nullable Metadata trailers) {
this(status, trailers, /*fillInStackTrace=*/ true);
}

StatusRuntimeException(Status status, @Nullable Metadata trailers, boolean fillInStackTrace) {
protected 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
Expand Down Expand Up @@ -86,4 +73,57 @@ public final Status getStatus() {
public final Metadata getTrailers() {
return trailers;
}

/**
* Builder for creating a {@link StatusRuntimeException}.
*
* @since 1.62.0
*/
public static class Builder {
private Status status;
private Metadata trailers = null;
private boolean fillInStackTrace = true;

/**
* Sets the status.
*
* @since 1.62.0
*/
public Builder setStatus(final Status status) {
this.status = status;
return this;
}

/**
* Sets the trailers.
*
* @since 1.62.0
*/
public Builder setTrailers(final Metadata trailers) {
this.trailers = trailers;
return this;
}

/**
* Sets whether to fill in the stack trace.
*
* @since 1.62.0
*/
public Builder setFillInStackTrace(final boolean fillInStackTrace) {
this.fillInStackTrace = fillInStackTrace;
return this;
}

/**
* Builds the exception.
*
* @since 1.62.0
*/
public StatusRuntimeException build() {
final StatusRuntimeException statusRuntimeException =
new StatusRuntimeException(status, trailers, fillInStackTrace);
statusRuntimeException.fillInStackTrace();
return statusRuntimeException;
}
}
}
11 changes: 7 additions & 4 deletions api/src/test/java/io/grpc/StatusExceptionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,30 +31,33 @@ public class StatusExceptionTest {
@Test
public void internalCtorRemovesStack() {
StackTraceElement[] trace =
new StatusException(Status.CANCELLED, null, false) {}.getStackTrace();
new StatusException.Builder().setStatus(Status.CANCELLED).setTrailers(null)
.setFillInStackTrace(false).build().getStackTrace();

assertThat(trace).isEmpty();
}

@Test
public void normalCtorKeepsStack() {
StackTraceElement[] trace =
new StatusException(Status.CANCELLED, null) {}.getStackTrace();
new StatusException.Builder().setStatus(Status.CANCELLED).setTrailers(null)
.build().getStackTrace();

assertThat(trace).isNotEmpty();
}

@Test
public void extendPreservesStack() {
StackTraceElement[] trace = new StatusException(Status.CANCELLED) {}.getStackTrace();
StackTraceElement[] trace =
new StatusException.Builder().setStatus(Status.CANCELLED).build().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()) {
StatusException exception = new StatusException(Status.CANCELLED, new Metadata(), true) {

@Override
public synchronized Throwable fillInStackTrace() {
Expand Down
24 changes: 14 additions & 10 deletions api/src/test/java/io/grpc/StatusRuntimeExceptionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,36 +31,40 @@ public class StatusRuntimeExceptionTest {
@Test
public void internalCtorRemovesStack() {
StackTraceElement[] trace =
new StatusRuntimeException(Status.CANCELLED, null, false) {}.getStackTrace();
new StatusRuntimeException.Builder().setStatus(Status.CANCELLED).setTrailers(null)
.setFillInStackTrace(false).build().getStackTrace();

assertThat(trace).isEmpty();
}

@Test
public void normalCtorKeepsStack() {
StackTraceElement[] trace =
new StatusRuntimeException(Status.CANCELLED, null) {}.getStackTrace();
new StatusRuntimeException.Builder().setStatus(Status.CANCELLED).setTrailers(null)
.build().getStackTrace();

assertThat(trace).isNotEmpty();
}

@Test
public void extendPreservesStack() {
StackTraceElement[] trace = new StatusRuntimeException(Status.CANCELLED) {}.getStackTrace();
StackTraceElement[] trace =
new StatusRuntimeException.Builder().setStatus(Status.CANCELLED).build().getStackTrace();

assertThat(trace).isNotEmpty();
}

@Test
public void extendAndOverridePreservesStack() {
final StackTraceElement element = new StackTraceElement("a", "b", "c", 4);
StatusRuntimeException error = new StatusRuntimeException(Status.CANCELLED, new Metadata()) {
@Override
public synchronized Throwable fillInStackTrace() {
setStackTrace(new StackTraceElement[]{element});
return this;
}
};
StatusRuntimeException error =
new StatusRuntimeException(Status.CANCELLED, new Metadata(), true) {
@Override
public synchronized Throwable fillInStackTrace() {
setStackTrace(new StackTraceElement[]{element});
return this;
}
};
assertThat(error.getStackTrace()).asList().containsExactly(element);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ public void messagesAvailable(StreamListener.MessageProducer producer) {
@Override
public void halfClosed() {
if (streamListenerMessageQueue.isEmpty()) {
throw new StatusRuntimeException(Status.INTERNAL.withDescription(
"Half close without request"));
throw new StatusRuntimeException.Builder().setStatus(Status.INTERNAL.withDescription(
"Half close without request")).build();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,8 @@ public void cancelThenSetCall() {
callExecutor, fakeClock.getScheduledExecutorService(), null);
delayedClientCall.start(listener, new Metadata());
delayedClientCall.request(1);
delayedClientCall.cancel("cancel", new StatusException(Status.CANCELLED));
delayedClientCall.cancel("cancel",
new StatusException.Builder().setStatus(Status.CANCELLED).build());
Runnable r = delayedClientCall.setCall(mockRealCall);
assertThat(r).isNull();
verify(mockRealCall, never()).start(any(Listener.class), any(Metadata.class));
Expand All @@ -187,7 +188,8 @@ public void setCallThenCancel() {
Runnable r = delayedClientCall.setCall(mockRealCall);
assertThat(r).isNotNull();
r.run();
delayedClientCall.cancel("cancel", new StatusException(Status.CANCELLED));
delayedClientCall.cancel("cancel",
new StatusException.Builder().setStatus(Status.CANCELLED).build());
@SuppressWarnings("unchecked")
ArgumentCaptor<Listener<Integer>> listenerCaptor = ArgumentCaptor.forClass(Listener.class);
verify(mockRealCall).start(listenerCaptor.capture(), any(Metadata.class));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.google.protobuf.Message;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import io.grpc.StatusRuntimeException;
import io.grpc.examples.routeguide.RouteGuideClient.TestHelper;
import io.grpc.examples.routeguide.RouteGuideGrpc.RouteGuideImplBase;
import io.grpc.inprocess.InProcessChannelBuilder;
Expand Down Expand Up @@ -135,7 +136,8 @@ public void getFeature(Point point, StreamObserver<Feature> responseObserver) {
public void getFeature_error() {
Point requestPoint = Point.newBuilder().setLatitude(-1).setLongitude(-1).build();
final AtomicReference<Point> pointDelivered = new AtomicReference<Point>();
final StatusRuntimeException fakeError = new StatusRuntimeException(Status.DATA_LOSS);
final StatusRuntimeException fakeError = new StatusRuntimeException.Builder()
.setStatus(Status.DATA_LOSS).build();

// implement the fake service
RouteGuideImplBase getFeatureImpl =
Expand Down Expand Up @@ -202,7 +204,8 @@ public void listFeatures_error() {
final Feature responseFeature1 =
Feature.newBuilder().setName("feature 1").build();
final AtomicReference<Rectangle> rectangleDelivered = new AtomicReference<Rectangle>();
final StatusRuntimeException fakeError = new StatusRuntimeException(Status.INVALID_ARGUMENT);
final StatusRuntimeException fakeError = new StatusRuntimeException.Builder()
.setStatus(Status.INVALID_ARGUMENT).build();

// implement the fake service
RouteGuideImplBase listFeaturesImpl =
Expand Down Expand Up @@ -311,7 +314,8 @@ public void recordRoute_serverError() throws Exception {
final Feature requestFeature1 =
Feature.newBuilder().setLocation(point1).build();
final List<Feature> features = Arrays.asList(requestFeature1);
final StatusRuntimeException fakeError = new StatusRuntimeException(Status.INVALID_ARGUMENT);
final StatusRuntimeException fakeError = new StatusRuntimeException.Builder()
.setStatus(Status.INVALID_ARGUMENT).build();

// implement the fake service
RouteGuideImplBase recordRouteImpl =
Expand Down Expand Up @@ -473,7 +477,8 @@ public void onCompleted() {
@Test
public void routeChat_errorResponse() throws Exception {
final List<RouteNote> notesDelivered = new ArrayList<>();
final StatusRuntimeException fakeError = new StatusRuntimeException(Status.PERMISSION_DENIED);
final StatusRuntimeException fakeError = new StatusRuntimeException.Builder()
.setStatus(Status.PERMISSION_DENIED).build();

// implement the fake service
RouteGuideImplBase routeChatImpl =
Expand Down
Loading