Skip to content

Commit ced1784

Browse files
core: remove potential this-escapes by extracting constructions of StatusException and StatusRuntimeException to builders
1 parent 3202370 commit ced1784

File tree

20 files changed

+223
-81
lines changed

20 files changed

+223
-81
lines changed

api/src/main/java/io/grpc/InternalStatus.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ private InternalStatus() {}
4444
@Internal
4545
public static final StatusRuntimeException asRuntimeException(Status status,
4646
@Nullable Metadata trailers, boolean fillInStackTrace) {
47-
return new StatusRuntimeException(status, trailers, fillInStackTrace);
47+
return new StatusRuntimeExceptionBuilder()
48+
.setStatus(status)
49+
.setTrailers(trailers)
50+
.setFillInStackTrace(fillInStackTrace)
51+
.build();
4852
}
4953
}

api/src/main/java/io/grpc/Status.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -522,30 +522,30 @@ public boolean isOk() {
522522
* to recover this {@link Status} instance when the returned exception is in the causal chain.
523523
*/
524524
public StatusRuntimeException asRuntimeException() {
525-
return new StatusRuntimeException(this);
525+
return new StatusRuntimeExceptionBuilder().setStatus(this).build();
526526
}
527527

528528
/**
529529
* Same as {@link #asRuntimeException()} but includes the provided trailers in the returned
530530
* exception.
531531
*/
532532
public StatusRuntimeException asRuntimeException(@Nullable Metadata trailers) {
533-
return new StatusRuntimeException(this, trailers);
533+
return new StatusRuntimeExceptionBuilder().setStatus(this).setTrailers(trailers).build();
534534
}
535535

536536
/**
537537
* Convert this {@link Status} to an {@link Exception}. Use {@link #fromThrowable}
538538
* to recover this {@link Status} instance when the returned exception is in the causal chain.
539539
*/
540540
public StatusException asException() {
541-
return new StatusException(this);
541+
return new StatusExceptionBuilder().setStatus(this).build();
542542
}
543543

544544
/**
545545
* Same as {@link #asException()} but includes the provided trailers in the returned exception.
546546
*/
547547
public StatusException asException(@Nullable Metadata trailers) {
548-
return new StatusException(this, trailers);
548+
return new StatusExceptionBuilder().setStatus(this).setTrailers(trailers).build();
549549
}
550550

551551
/** A string representation of the status useful for debugging. */

api/src/main/java/io/grpc/StatusException.java

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,30 +30,16 @@ public class StatusException extends Exception {
3030
private final boolean fillInStackTrace;
3131

3232
/**
33-
* Constructs an exception with both a status. See also {@link Status#asException()}.
33+
* Constructs an exception with status, trailers, and whether to fill in the stack trace.
34+
* See also {@link Status#asException()} and {@link Status#asException(Metadata)}.
3435
*
3536
* @since 1.0.0
3637
*/
37-
public StatusException(Status status) {
38-
this(status, null);
39-
}
40-
41-
/**
42-
* Constructs an exception with both a status and trailers. See also
43-
* {@link Status#asException(Metadata)}.
44-
*
45-
* @since 1.0.0
46-
*/
47-
public StatusException(Status status, @Nullable Metadata trailers) {
48-
this(status, trailers, /*fillInStackTrace=*/ true);
49-
}
50-
5138
StatusException(Status status, @Nullable Metadata trailers, boolean fillInStackTrace) {
5239
super(Status.formatThrowableMessage(status), status.getCause());
5340
this.status = status;
5441
this.trailers = trailers;
5542
this.fillInStackTrace = fillInStackTrace;
56-
fillInStackTrace();
5743
}
5844

5945
@Override
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright 2021 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc;
18+
19+
/**
20+
* Builder for creating a {@link StatusException}.
21+
*
22+
* @since 1.62.0
23+
*/
24+
public class StatusExceptionBuilder {
25+
private Status status;
26+
private Metadata trailers = null;
27+
private boolean fillInStackTrace = true;
28+
29+
/**
30+
* Sets the status.
31+
*
32+
* @since 1.62.0
33+
*/
34+
public StatusExceptionBuilder setStatus(final Status status) {
35+
this.status = status;
36+
return this;
37+
}
38+
39+
/**
40+
* Sets the trailers.
41+
*
42+
* @since 1.62.0
43+
*/
44+
public StatusExceptionBuilder setTrailers(final Metadata trailers) {
45+
this.trailers = trailers;
46+
return this;
47+
}
48+
49+
/**
50+
* Sets whether to fill in the stack trace.
51+
*
52+
* @since 1.62.0
53+
*/
54+
public StatusExceptionBuilder setFillInStackTrace(final boolean fillInStackTrace) {
55+
this.fillInStackTrace = fillInStackTrace;
56+
return this;
57+
}
58+
59+
/**
60+
* Builds the exception.
61+
*
62+
* @since 1.62.0
63+
*/
64+
public StatusException build() {
65+
final StatusException statusException = new StatusException(status, trailers, fillInStackTrace);
66+
statusException.fillInStackTrace();
67+
return statusException;
68+
}
69+
}

api/src/main/java/io/grpc/StatusRuntimeException.java

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,30 +32,16 @@ public class StatusRuntimeException extends RuntimeException {
3232
private final boolean fillInStackTrace;
3333

3434
/**
35-
* Constructs the exception with both a status. See also {@link Status#asRuntimeException()}.
35+
* Constructs an exception with status, trailers, and whether to fill in the stack trace.
36+
* See also {@link Status#asRuntimeException()} and {@link Status#asRuntimeException(Metadata)}.
3637
*
3738
* @since 1.0.0
3839
*/
39-
public StatusRuntimeException(Status status) {
40-
this(status, null);
41-
}
42-
43-
/**
44-
* Constructs the exception with both a status and trailers. See also {@link
45-
* Status#asRuntimeException(Metadata)}.
46-
*
47-
* @since 1.0.0
48-
*/
49-
public StatusRuntimeException(Status status, @Nullable Metadata trailers) {
50-
this(status, trailers, /*fillInStackTrace=*/ true);
51-
}
52-
5340
StatusRuntimeException(Status status, @Nullable Metadata trailers, boolean fillInStackTrace) {
5441
super(Status.formatThrowableMessage(status), status.getCause());
5542
this.status = status;
5643
this.trailers = trailers;
5744
this.fillInStackTrace = fillInStackTrace;
58-
fillInStackTrace();
5945
}
6046

6147
@Override
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2021 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc;
18+
19+
/**
20+
* Builder for creating a {@link StatusRuntimeException}.
21+
*
22+
* @since 1.62.0
23+
*/
24+
public class StatusRuntimeExceptionBuilder {
25+
private Status status;
26+
private Metadata trailers = null;
27+
private boolean fillInStackTrace = true;
28+
29+
/**
30+
* Sets the status.
31+
*
32+
* @since 1.62.0
33+
*/
34+
public StatusRuntimeExceptionBuilder setStatus(final Status status) {
35+
this.status = status;
36+
return this;
37+
}
38+
39+
/**
40+
* Sets the trailers.
41+
*
42+
* @since 1.62.0
43+
*/
44+
public StatusRuntimeExceptionBuilder setTrailers(final Metadata trailers) {
45+
this.trailers = trailers;
46+
return this;
47+
}
48+
49+
/**
50+
* Sets whether to fill in the stack trace.
51+
*
52+
* @since 1.62.0
53+
*/
54+
public StatusRuntimeExceptionBuilder setFillInStackTrace(final boolean fillInStackTrace) {
55+
this.fillInStackTrace = fillInStackTrace;
56+
return this;
57+
}
58+
59+
/**
60+
* Builds the exception.
61+
*
62+
* @since 1.62.0
63+
*/
64+
public StatusRuntimeException build() {
65+
final StatusRuntimeException statusRuntimeException =
66+
new StatusRuntimeException(status, trailers, fillInStackTrace);
67+
statusRuntimeException.fillInStackTrace();
68+
return statusRuntimeException;
69+
}
70+
}

api/src/test/java/io/grpc/StatusExceptionTest.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,30 +31,33 @@ public class StatusExceptionTest {
3131
@Test
3232
public void internalCtorRemovesStack() {
3333
StackTraceElement[] trace =
34-
new StatusException(Status.CANCELLED, null, false) {}.getStackTrace();
34+
new StatusExceptionBuilder().setStatus(Status.CANCELLED).setTrailers(null)
35+
.setFillInStackTrace(false).build().getStackTrace();
3536

3637
assertThat(trace).isEmpty();
3738
}
3839

3940
@Test
4041
public void normalCtorKeepsStack() {
4142
StackTraceElement[] trace =
42-
new StatusException(Status.CANCELLED, null) {}.getStackTrace();
43+
new StatusExceptionBuilder().setStatus(Status.CANCELLED).setTrailers(null)
44+
.build().getStackTrace();
4345

4446
assertThat(trace).isNotEmpty();
4547
}
4648

4749
@Test
4850
public void extendPreservesStack() {
49-
StackTraceElement[] trace = new StatusException(Status.CANCELLED) {}.getStackTrace();
51+
StackTraceElement[] trace =
52+
new StatusExceptionBuilder().setStatus(Status.CANCELLED).build().getStackTrace();
5053

5154
assertThat(trace).isNotEmpty();
5255
}
5356

5457
@Test
5558
public void extendAndOverridePreservesStack() {
5659
final StackTraceElement element = new StackTraceElement("a", "b", "c", 4);
57-
StatusException exception = new StatusException(Status.CANCELLED, new Metadata()) {
60+
StatusException exception = new StatusException(Status.CANCELLED, new Metadata(), true) {
5861

5962
@Override
6063
public synchronized Throwable fillInStackTrace() {

api/src/test/java/io/grpc/StatusRuntimeExceptionTest.java

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,36 +31,40 @@ public class StatusRuntimeExceptionTest {
3131
@Test
3232
public void internalCtorRemovesStack() {
3333
StackTraceElement[] trace =
34-
new StatusRuntimeException(Status.CANCELLED, null, false) {}.getStackTrace();
34+
new StatusRuntimeExceptionBuilder().setStatus(Status.CANCELLED).setTrailers(null)
35+
.setFillInStackTrace(false).build().getStackTrace();
3536

3637
assertThat(trace).isEmpty();
3738
}
3839

3940
@Test
4041
public void normalCtorKeepsStack() {
4142
StackTraceElement[] trace =
42-
new StatusRuntimeException(Status.CANCELLED, null) {}.getStackTrace();
43+
new StatusRuntimeExceptionBuilder().setStatus(Status.CANCELLED).setTrailers(null)
44+
.build().getStackTrace();
4345

4446
assertThat(trace).isNotEmpty();
4547
}
4648

4749
@Test
4850
public void extendPreservesStack() {
49-
StackTraceElement[] trace = new StatusRuntimeException(Status.CANCELLED) {}.getStackTrace();
51+
StackTraceElement[] trace =
52+
new StatusRuntimeExceptionBuilder().setStatus(Status.CANCELLED).build().getStackTrace();
5053

5154
assertThat(trace).isNotEmpty();
5255
}
5356

5457
@Test
5558
public void extendAndOverridePreservesStack() {
5659
final StackTraceElement element = new StackTraceElement("a", "b", "c", 4);
57-
StatusRuntimeException error = new StatusRuntimeException(Status.CANCELLED, new Metadata()) {
58-
@Override
59-
public synchronized Throwable fillInStackTrace() {
60-
setStackTrace(new StackTraceElement[]{element});
61-
return this;
62-
}
63-
};
60+
StatusRuntimeException error =
61+
new StatusRuntimeException(Status.CANCELLED, new Metadata(), true) {
62+
@Override
63+
public synchronized Throwable fillInStackTrace() {
64+
setStackTrace(new StackTraceElement[]{element});
65+
return this;
66+
}
67+
};
6468
assertThat(error.getStackTrace()).asList().containsExactly(element);
6569
}
6670
}

core/src/test/java/io/grpc/internal/AbstractServerStreamTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
import io.grpc.InternalStatus;
3535
import io.grpc.Metadata;
3636
import io.grpc.Status;
37-
import io.grpc.StatusRuntimeException;
37+
import io.grpc.StatusRuntimeExceptionBuilder;
3838
import io.grpc.internal.AbstractServerStream.TransportState;
3939
import io.grpc.internal.MessageFramerTest.ByteWritableBuffer;
4040
import java.io.ByteArrayInputStream;
@@ -127,8 +127,8 @@ public void messagesAvailable(StreamListener.MessageProducer producer) {
127127
@Override
128128
public void halfClosed() {
129129
if (streamListenerMessageQueue.isEmpty()) {
130-
throw new StatusRuntimeException(Status.INTERNAL.withDescription(
131-
"Half close without request"));
130+
throw new StatusRuntimeExceptionBuilder().setStatus(Status.INTERNAL.withDescription(
131+
"Half close without request")).build();
132132
}
133133
}
134134

core/src/test/java/io/grpc/internal/DelayedClientCallTest.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
import io.grpc.ForwardingTestUtil;
3434
import io.grpc.Metadata;
3535
import io.grpc.Status;
36-
import io.grpc.StatusException;
36+
import io.grpc.StatusExceptionBuilder;
3737
import java.lang.reflect.Method;
3838
import java.lang.reflect.Modifier;
3939
import java.util.Arrays;
@@ -168,7 +168,8 @@ public void cancelThenSetCall() {
168168
callExecutor, fakeClock.getScheduledExecutorService(), null);
169169
delayedClientCall.start(listener, new Metadata());
170170
delayedClientCall.request(1);
171-
delayedClientCall.cancel("cancel", new StatusException(Status.CANCELLED));
171+
delayedClientCall.cancel("cancel",
172+
new StatusExceptionBuilder().setStatus(Status.CANCELLED).build());
172173
Runnable r = delayedClientCall.setCall(mockRealCall);
173174
assertThat(r).isNull();
174175
verify(mockRealCall, never()).start(any(Listener.class), any(Metadata.class));
@@ -187,7 +188,8 @@ public void setCallThenCancel() {
187188
Runnable r = delayedClientCall.setCall(mockRealCall);
188189
assertThat(r).isNotNull();
189190
r.run();
190-
delayedClientCall.cancel("cancel", new StatusException(Status.CANCELLED));
191+
delayedClientCall.cancel("cancel",
192+
new StatusExceptionBuilder().setStatus(Status.CANCELLED).build());
191193
@SuppressWarnings("unchecked")
192194
ArgumentCaptor<Listener<Integer>> listenerCaptor = ArgumentCaptor.forClass(Listener.class);
193195
verify(mockRealCall).start(listenerCaptor.capture(), any(Metadata.class));

0 commit comments

Comments
 (0)