Skip to content

Commit 912941e

Browse files
committed
review feedback
1 parent b2f7ea8 commit 912941e

File tree

7 files changed

+133
-19
lines changed

7 files changed

+133
-19
lines changed

grpc-server-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/server/advice/GrpcAdviceExceptionHandler.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,11 @@ private Object invokeMappedMethodSafely(
108108
Method mappedMethod,
109109
Object instanceOfMappedMethod,
110110
Object[] instancedParams) throws Throwable {
111-
Object statusThrowable = null;
112111
try {
113-
statusThrowable = mappedMethod.invoke(instanceOfMappedMethod, instancedParams);
112+
return mappedMethod.invoke(instanceOfMappedMethod, instancedParams);
114113
} catch (InvocationTargetException | IllegalAccessException e) {
115114
throw e.getCause(); // throw the exception thrown by implementation
116115
}
117-
return statusThrowable;
118116
}
119117

120118
}

grpc-server-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/server/advice/GrpcAdviceExceptionListener.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
* @see GrpcAdviceExceptionHandler
4040
*/
4141
@Slf4j
42-
class GrpcAdviceExceptionListener<ReqT, RespT> extends SimpleForwardingServerCallListener<ReqT> {
42+
public class GrpcAdviceExceptionListener<ReqT, RespT> extends SimpleForwardingServerCallListener<ReqT> {
4343

4444
private final GrpcAdviceExceptionHandler exceptionHandler;
4545
private final ServerCall<ReqT, RespT> serverCall;
@@ -66,7 +66,7 @@ public void onHalfClose() {
6666
private void handleCaughtException(Exception exception) {
6767
try {
6868
Object mappedReturnType = exceptionHandler.handleThrownException(exception);
69-
Status status = resolveStatus(mappedReturnType);
69+
Status status = resolveStatus(mappedReturnType).withCause(exception);
7070
Metadata metadata = resolveMetadata(mappedReturnType);
7171

7272
serverCall.close(status, metadata);
@@ -100,8 +100,9 @@ private Metadata resolveMetadata(Object mappedReturnType) {
100100
}
101101

102102
private void handleThrownExceptionByImplementation(Throwable throwable) {
103+
log.error("Exception thrown during invocation of annotated @GrpcExceptionHandler method: ", throwable);
103104
serverCall.close(Status.INTERNAL.withCause(throwable)
104-
.withDescription(throwable.getMessage()), new Metadata());
105+
.withDescription("There was a server error trying to handle an exception"), new Metadata());
105106
}
106107

107108
}

grpc-server-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ net.devh.boot.grpc.server.autoconfigure.GrpcServerFactoryAutoConfiguration,\
88
net.devh.boot.grpc.server.autoconfigure.GrpcServerSecurityAutoConfiguration,\
99
net.devh.boot.grpc.server.autoconfigure.GrpcServerMetricAutoConfiguration,\
1010
net.devh.boot.grpc.server.autoconfigure.GrpcServerTraceAutoConfiguration,\
11-
net.devh.boot.grpc.server.autoconfigure.GrpcAdviceAutoConfiguration
11+
net.devh.boot.grpc.server.autoconfigure.GrpcAdviceAutoConfiguration

tests/src/test/java/net/devh/boot/grpc/test/advice/AdviceExceptionHandlingTest.java

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,37 @@
1717

1818
package net.devh.boot.grpc.test.advice;
1919

20+
import static org.assertj.core.api.Assertions.assertThat;
21+
2022
import java.security.AccessControlException;
2123

24+
import org.assertj.core.groups.Tuple;
2225
import org.junit.jupiter.api.AfterAll;
2326
import org.junit.jupiter.api.BeforeAll;
27+
import org.junit.jupiter.api.BeforeEach;
2428
import org.junit.jupiter.api.Test;
2529
import org.springframework.beans.factory.annotation.Autowired;
2630
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
2731
import org.springframework.boot.test.context.SpringBootTest;
2832
import org.springframework.test.annotation.DirtiesContext;
2933
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
3034

35+
import ch.qos.logback.classic.Level;
36+
import ch.qos.logback.classic.spi.ILoggingEvent;
37+
import ch.qos.logback.core.read.ListAppender;
3138
import io.grpc.Metadata;
3239
import io.grpc.Status;
3340
import lombok.extern.slf4j.Slf4j;
41+
import net.devh.boot.grpc.server.advice.GrpcAdviceExceptionListener;
3442
import net.devh.boot.grpc.server.autoconfigure.GrpcAdviceAutoConfiguration;
3543
import net.devh.boot.grpc.test.config.BaseAutoConfiguration;
3644
import net.devh.boot.grpc.test.config.GrpcAdviceConfig;
3745
import net.devh.boot.grpc.test.config.GrpcAdviceConfig.TestAdviceWithMetadata.FirstLevelException;
3846
import net.devh.boot.grpc.test.config.GrpcAdviceConfig.TestAdviceWithMetadata.MyRootRuntimeException;
47+
import net.devh.boot.grpc.test.config.GrpcAdviceConfig.TestAdviceWithMetadata.StatusMappingException;
3948
import net.devh.boot.grpc.test.config.GrpcAdviceConfig.TestGrpcAdviceService;
4049
import net.devh.boot.grpc.test.config.InProcessConfiguration;
50+
import net.devh.boot.grpc.test.util.LoggerTestUtil;
4151

4252
/**
4353
* A test checking that the server and client can start and connect to each other with minimal config and a exception
@@ -56,10 +66,16 @@
5666
@DirtiesContext
5767
class AdviceExceptionHandlingTest extends AbstractSimpleServerClientTest {
5868

69+
private ListAppender<ILoggingEvent> loggingEventListAppender;
5970

6071
@Autowired
6172
private TestGrpcAdviceService testGrpcAdviceService;
6273

74+
@BeforeEach
75+
void setup() {
76+
loggingEventListAppender = LoggerTestUtil.getListAppenderForClasses(GrpcAdviceExceptionListener.class);
77+
}
78+
6379
@Test
6480
@DirtiesContext
6581
void testThrownIllegalArgumentException_IsMappedAsStatus() {
@@ -98,14 +114,21 @@ void testThrownClassCastException_IsMappedAsStatusRuntimeExceptionAndWithMetadat
98114

99115
@Test
100116
@DirtiesContext
101-
void testThrownMyRootRuntimeException_IsNotMapped() {
117+
void testThrownMyRootRuntimeException_IsNotMappedAndResultsInInvocationException() {
102118

103119
MyRootRuntimeException exceptionToMap = new MyRootRuntimeException("Trigger Advice");
104120
testGrpcAdviceService.setExceptionToSimulate(exceptionToMap);
105-
Status expectedStatus = Status.INTERNAL.withDescription(exceptionToMap.getMessage());
121+
Status expectedStatus =
122+
Status.INTERNAL.withDescription("There was a server error trying to handle an exception");
106123
Metadata metadata = new Metadata();
107124

108125
testGrpcCallAndVerifyMappedException(expectedStatus, metadata);
126+
assertThat(loggingEventListAppender.list)
127+
.extracting(ILoggingEvent::getMessage, ILoggingEvent::getLevel)
128+
.contains(Tuple.tuple("Exception caught during gRPC execution: ", Level.ERROR))
129+
.contains(Tuple.tuple(
130+
"Exception thrown during invocation of annotated @GrpcExceptionHandler method: ",
131+
Level.ERROR));
109132
}
110133

111134
@Test
@@ -120,6 +143,26 @@ void testThrownFirstLevelException_IsMappedAsStatusExceptionWithMetadata() {
120143
testGrpcCallAndVerifyMappedException(expectedStatus, metadata);
121144
}
122145

146+
@Test
147+
@DirtiesContext
148+
void testThrownStatusMappingException_IsResolvedAsInternalServerError() {
149+
150+
StatusMappingException exceptionToMap = new StatusMappingException("Trigger Advice");
151+
testGrpcAdviceService.setExceptionToSimulate(exceptionToMap);
152+
Status expectedStatus =
153+
Status.INTERNAL.withDescription("There was a server error trying to handle an exception");
154+
Metadata metadata = new Metadata();
155+
156+
testGrpcCallAndVerifyMappedException(expectedStatus, metadata);
157+
158+
assertThat(loggingEventListAppender.list)
159+
.extracting(ILoggingEvent::getMessage, ILoggingEvent::getLevel)
160+
.contains(Tuple.tuple("Exception caught during gRPC execution: ", Level.ERROR))
161+
.contains(Tuple.tuple(
162+
"Exception thrown during invocation of annotated @GrpcExceptionHandler method: ",
163+
Level.ERROR));
164+
}
165+
123166

124167

125168
@BeforeAll

tests/src/test/java/net/devh/boot/grpc/test/advice/AdviceIsPresentAutoConfigurationTest.java

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
package net.devh.boot.grpc.test.advice;
1919

20+
import static org.assertj.core.api.Assertions.assertThat;
21+
2022
import java.lang.reflect.Method;
2123
import java.util.Arrays;
2224
import java.util.Collection;
@@ -27,9 +29,9 @@
2729
import java.util.stream.Collectors;
2830
import java.util.stream.Stream;
2931

30-
import org.assertj.core.api.Assertions;
3132
import org.junit.jupiter.api.AfterAll;
3233
import org.junit.jupiter.api.BeforeAll;
34+
import org.junit.jupiter.api.BeforeEach;
3335
import org.junit.jupiter.api.Test;
3436
import org.springframework.beans.factory.annotation.Autowired;
3537
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
@@ -59,7 +61,8 @@
5961
class AdviceIsPresentAutoConfigurationTest {
6062

6163
private static final int ADVICE_CLASSES = 2;
62-
private static final int ADVICE_METHODS = 4;
64+
private static final int ADVICE_METHODS = 5;
65+
6366

6467
@Autowired
6568
private GrpcAdviceDiscoverer grpcAdviceDiscoverer;
@@ -69,27 +72,26 @@ class AdviceIsPresentAutoConfigurationTest {
6972
@Autowired
7073
private TestAdviceWithMetadata testAdviceWithMetadata;
7174

75+
@BeforeEach
76+
void setup() {}
77+
7278
@Test
7379
@DirtiesContext
7480
void testAdviceIsPresentWithExceptionMapping() {
7581
log.info("--- Starting tests with advice auto discovery ---");
7682

7783
Map<String, Object> expectedAdviceBeans = new HashMap<>();
78-
expectedAdviceBeans.put(
79-
"net.devh.boot.grpc.test.config.GrpcAdviceConfig$TestAdviceWithOutMetadata",
80-
testAdviceWithOutMetadata);
81-
expectedAdviceBeans.put(
82-
"net.devh.boot.grpc.test.config.GrpcAdviceConfig$TestAdviceWithMetadata",
83-
testAdviceWithMetadata);
84+
expectedAdviceBeans.put(TestAdviceWithOutMetadata.class.getName(), testAdviceWithOutMetadata);
85+
expectedAdviceBeans.put(TestAdviceWithMetadata.class.getName(), testAdviceWithMetadata);
8486
Set<Method> expectedAdviceMethods = expectedMethods();
8587

8688
Map<String, Object> actualAdviceBeans = grpcAdviceDiscoverer.getAnnotatedBeans();
8789
Set<Method> actualAdviceMethods = grpcAdviceDiscoverer.getAnnotatedMethods();
8890

89-
Assertions.assertThat(actualAdviceBeans)
91+
assertThat(actualAdviceBeans)
9092
.hasSize(ADVICE_CLASSES)
9193
.containsExactlyInAnyOrderEntriesOf(expectedAdviceBeans);
92-
Assertions.assertThat(actualAdviceMethods)
94+
assertThat(actualAdviceMethods)
9395
.hasSize(ADVICE_METHODS)
9496
.containsExactlyInAnyOrderElementsOf(expectedAdviceMethods);
9597
}

tests/src/test/java/net/devh/boot/grpc/test/config/GrpcAdviceConfig.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ public StatusRuntimeException handleClassCastException() {
9797
return status.asRuntimeException(metadata);
9898
}
9999

100+
@GrpcExceptionHandler
101+
public StatusRuntimeException handleStatusMappingException(StatusMappingException e) {
102+
103+
throw new NullPointerException("Simulate developer error");
104+
}
105+
100106

101107
public static class MyRootRuntimeException extends RuntimeException {
102108

@@ -112,6 +118,13 @@ public FirstLevelException(String msg) {
112118
}
113119
}
114120

121+
public static class StatusMappingException extends RuntimeException {
122+
123+
public StatusMappingException(String msg) {
124+
super(msg);
125+
}
126+
}
127+
115128
}
116129

117130

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright (c) 2016-2020 Michael Zhang <[email protected]>
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
7+
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
8+
*
9+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10+
* Software.
11+
*
12+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16+
*/
17+
18+
package net.devh.boot.grpc.test.util;
19+
20+
import java.util.Arrays;
21+
22+
import org.slf4j.LoggerFactory;
23+
import org.springframework.lang.Nullable;
24+
25+
import ch.qos.logback.classic.Logger;
26+
import ch.qos.logback.classic.spi.ILoggingEvent;
27+
import ch.qos.logback.core.read.ListAppender;
28+
29+
/**
30+
* Class to test proper @Slf4j logging.
31+
*
32+
* @author Andjelko Perisic ([email protected])
33+
*/
34+
public class LoggerTestUtil {
35+
36+
private LoggerTestUtil() {
37+
throw new UnsupportedOperationException("Util class not to be instantiated.");
38+
}
39+
40+
41+
public static ListAppender<ILoggingEvent> getListAppenderForClasses(@Nullable Class<?>... classList) {
42+
43+
ListAppender<ILoggingEvent> loggingEventListAppender = new ListAppender<>();
44+
loggingEventListAppender.start();
45+
46+
if (classList == null) {
47+
return loggingEventListAppender;
48+
}
49+
50+
Arrays.stream(classList)
51+
.map(clazz -> (Logger) LoggerFactory.getLogger(clazz))
52+
.forEach(log -> log.addAppender(loggingEventListAppender));
53+
54+
return loggingEventListAppender;
55+
}
56+
57+
}

0 commit comments

Comments
 (0)