Skip to content

Commit 19fc751

Browse files
author
Alexander Furer
committed
deprecate GRpcErrorHandler in favor of GRpcExceptionHandler and GRpcServiceAdvice
1 parent 4efb349 commit 19fc751

22 files changed

+520
-198
lines changed

grpc-spring-boot-starter-demo/src/customSecurityTest/java/org/lognet/springboot/grpc/auth/CustomSecurityTest.java

+1-6
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import io.grpc.StatusRuntimeException;
66
import io.grpc.examples.SecuredGreeterGrpc;
77
import org.hamcrest.Matchers;
8-
import org.junit.Assert;
98
import org.junit.Test;
109
import org.junit.runner.RunWith;
1110
import org.lognet.springboot.grpc.GrpcServerTestBase;
@@ -28,7 +27,6 @@
2827

2928
import static org.hamcrest.MatcherAssert.assertThat;
3029
import static org.junit.Assert.assertNotNull;
31-
3230
import static org.junit.Assert.assertThrows;
3331
import static org.junit.Assert.assertTrue;
3432
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE;
@@ -48,8 +46,6 @@ public class DemoGrpcSecurityConfig extends GrpcSecurityConfigurerAdapter {
4846

4947
@Override
5048
public void configure(GrpcSecurity builder) throws Exception {
51-
52-
5349
builder.authorizeRequests()
5450
.withSecuredAnnotation()
5551
.authenticationSchemeSelector(scheme ->
@@ -64,7 +60,6 @@ public void configure(GrpcSecurity builder) throws Exception {
6460
.authenticationProvider(new TestingAuthenticationProvider());
6561
}
6662

67-
6863
}
6964

7065
}
@@ -88,7 +83,7 @@ public void customSchemeAccessDeniedTest() {
8883

8984
}
9085

91-
private String invoke(String userName, String authority) {
86+
private String invoke(String userName, String authority) {
9287
AuthCallCredentials callCredentials = new AuthCallCredentials(
9388
AuthHeader.builder().authScheme(MY_CUSTOM_SCHEME_NAME).tokenSupplier(() ->
9489
ByteBuffer.wrap(String.format("%s#%s", userName, authority).getBytes()))

grpc-spring-boot-starter/src/main/java/org/lognet/springboot/grpc/FailureHandlingServerInterceptor.java

-57
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package org.lognet.springboot.grpc;
2+
3+
import io.grpc.Metadata;
4+
import io.grpc.ServerCall;
5+
import io.grpc.Status;
6+
import org.lognet.springboot.grpc.recovery.GRpcExceptionHandlerMethodResolver;
7+
import org.lognet.springboot.grpc.recovery.GRpcExceptionScope;
8+
import org.lognet.springboot.grpc.recovery.GRpcRuntimeExceptionWrapper;
9+
import org.lognet.springboot.grpc.recovery.HandlerMethod;
10+
11+
import java.util.Optional;
12+
import java.util.function.Consumer;
13+
14+
public class FailureHandlingSupport {
15+
16+
private final GRpcExceptionHandlerMethodResolver methodResolver;
17+
18+
public FailureHandlingSupport(GRpcExceptionHandlerMethodResolver methodResolver) {
19+
this.methodResolver = methodResolver;
20+
}
21+
22+
public void closeCall(RuntimeException e, ServerCall<?, ?> call, Metadata headers, Consumer<GRpcExceptionScope.GRpcExceptionScopeBuilder> customizer) throws RuntimeException {
23+
24+
25+
final Optional<HandlerMethod> handlerMethod = methodResolver.resolveMethodByThrowable(call.getMethodDescriptor().getServiceName(), e);
26+
if (handlerMethod.isPresent()) {
27+
final GRpcExceptionScope.GRpcExceptionScopeBuilder exceptionScopeBuilder = GRpcExceptionScope.builder()
28+
.callHeaders(headers)
29+
.methodCallAttributes(call.getAttributes())
30+
.methodDescriptor(call.getMethodDescriptor())
31+
.hint(GRpcRuntimeExceptionWrapper.getHint(e));
32+
customizer.accept(exceptionScopeBuilder);
33+
34+
final GRpcExceptionScope excScope = exceptionScopeBuilder.build();
35+
36+
final HandlerMethod handler = handlerMethod.get();
37+
38+
39+
Status statusToSend = Status.INTERNAL;
40+
try {
41+
statusToSend = handler.invoke(GRpcRuntimeExceptionWrapper.unwrap(e),excScope );
42+
}catch (Exception handlerException){
43+
44+
org.slf4j.LoggerFactory.getLogger(this.getClass())
45+
.error("Caught exception while executing handler method {}, returning {} status.",
46+
handler.getMethod(),
47+
statusToSend,
48+
handlerException);
49+
50+
}
51+
call.close(statusToSend, excScope.getResponseHeaders());
52+
53+
54+
} else {
55+
call.close(Status.INTERNAL,new Metadata());
56+
}
57+
58+
}
59+
60+
61+
62+
}

grpc-spring-boot-starter/src/main/java/org/lognet/springboot/grpc/GRpcErrorHandler.java

+7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44
import io.grpc.Status;
55
import lombok.extern.slf4j.Slf4j;
66

7+
8+
9+
/**
10+
*
11+
* Please use {@link org.lognet.springboot.grpc.recovery.GRpcExceptionHandler} instead
12+
*/
13+
@Deprecated
714
@Slf4j
815
public class GRpcErrorHandler {
916

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.lognet.springboot.grpc;
2+
3+
import io.grpc.ForwardingServerCallListener;
4+
import io.grpc.ServerCall;
5+
6+
7+
public class MessageBlockingServerCallListener<R> extends ForwardingServerCallListener.SimpleForwardingServerCallListener<R> {
8+
9+
10+
private volatile boolean messageBlocked = false;
11+
12+
13+
public MessageBlockingServerCallListener(ServerCall.Listener<R> delegate) {
14+
super(delegate);
15+
}
16+
17+
@Override
18+
public void onHalfClose() {
19+
// If the message was blocked, downstream never had a chance to react to it. Hence, the half-close signal would look like
20+
// an error to them. So we do not propagate the signal in that case.
21+
if (!messageBlocked) {
22+
super.onHalfClose();
23+
}
24+
}
25+
26+
protected void blockMessage() {
27+
messageBlocked = true;
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.lognet.springboot.grpc.autoconfigure;
2+
3+
4+
import org.springframework.context.annotation.Conditional;
5+
6+
import java.lang.annotation.Documented;
7+
import java.lang.annotation.ElementType;
8+
import java.lang.annotation.Retention;
9+
import java.lang.annotation.RetentionPolicy;
10+
import java.lang.annotation.Target;
11+
12+
@Target({ ElementType.TYPE , ElementType.METHOD })
13+
@Retention(RetentionPolicy.RUNTIME)
14+
@Documented
15+
@Conditional(OnMissingErrorHandlerCondition.class)
16+
public @interface ConditionalOnMissingErrorHandler {
17+
Class<? extends Throwable> value();
18+
}

grpc-spring-boot-starter/src/main/java/org/lognet/springboot/grpc/autoconfigure/GRpcAutoConfiguration.java

+14-7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import io.grpc.ServerBuilder;
44
import io.grpc.inprocess.InProcessServerBuilder;
5+
import org.lognet.springboot.grpc.FailureHandlingSupport;
56
import org.lognet.springboot.grpc.GRpcGlobalInterceptor;
67
import org.lognet.springboot.grpc.GRpcServerBuilderConfigurer;
78
import org.lognet.springboot.grpc.GRpcServerRunner;
@@ -43,7 +44,6 @@
4344
@ConditionalOnBean(annotation = GRpcService.class)
4445
@EnableConfigurationProperties({GRpcServerProperties.class})
4546
@Import({GRpcValidationConfiguration.class,
46-
4747
NettyServerBuilderSelector.class,
4848
DefaultHealthStatusService.class
4949
})
@@ -59,26 +59,33 @@ public GRpcServerRunner grpcServerRunner(@Qualifier("grpcInternalConfigurator")
5959
return new GRpcServerRunner(configurator, serverBuilder);
6060
}
6161

62-
6362
@Bean
6463
@ConditionalOnProperty(prefix = "grpc", name = "inProcessServerName")
6564
public GRpcServerRunner grpcInprocessServerRunner(@Qualifier("grpcInternalConfigurator") Consumer<ServerBuilder<?>> configurator) {
6665
return new GRpcServerRunner(configurator, InProcessServerBuilder.forName(grpcServerProperties.getInProcessServerName()));
6766
}
6867

69-
7068
@Bean
7169
public GRpcServicesRegistry grpcServicesRegistry() {
7270
return new GRpcServicesRegistry();
7371
}
7472

7573
@Bean
76-
@GRpcGlobalInterceptor
77-
public GRpcExceptionHandlerInterceptor exceptionHandlerInterceptor(GRpcServicesRegistry gRpcServicesRegistry, ApplicationContext applicationContext) {
74+
public GRpcExceptionHandlerMethodResolver exceptionHandlerMethodResolver(GRpcServicesRegistry gRpcServicesRegistry, ApplicationContext applicationContext){
7875
final Collection<Object> advices = applicationContext.getBeansWithAnnotation(GRpcServiceAdvice.class).values();
76+
return new GRpcExceptionHandlerMethodResolver(gRpcServicesRegistry, advices);
77+
}
7978

80-
final GRpcExceptionHandlerMethodResolver methodResolver = new GRpcExceptionHandlerMethodResolver(gRpcServicesRegistry, advices);
81-
return new GRpcExceptionHandlerInterceptor(methodResolver);
79+
@Bean
80+
public FailureHandlingSupport failureHandlingSupport(GRpcExceptionHandlerMethodResolver methodResolver){
81+
return new FailureHandlingSupport(methodResolver);
82+
}
83+
84+
@Bean
85+
@GRpcGlobalInterceptor
86+
public GRpcExceptionHandlerInterceptor exceptionHandlerInterceptor(FailureHandlingSupport failureHandlingSupport,
87+
GRpcExceptionHandlerMethodResolver methodResolver) {
88+
return new GRpcExceptionHandlerInterceptor(methodResolver,failureHandlingSupport);
8289
}
8390

8491
@Bean
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
package org.lognet.springboot.grpc.autoconfigure;
22

3+
import io.grpc.Status;
4+
import lombok.extern.slf4j.Slf4j;
5+
import org.lognet.springboot.grpc.FailureHandlingSupport;
6+
import org.lognet.springboot.grpc.GRpcErrorHandler;
37
import org.lognet.springboot.grpc.GRpcGlobalInterceptor;
8+
import org.lognet.springboot.grpc.recovery.ErrorHandlerAdapter;
9+
import org.lognet.springboot.grpc.recovery.GRpcExceptionHandler;
10+
import org.lognet.springboot.grpc.recovery.GRpcExceptionScope;
11+
import org.lognet.springboot.grpc.recovery.GRpcServiceAdvice;
412
import org.lognet.springboot.grpc.validation.ValidatingInterceptor;
513
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
614
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -9,17 +17,45 @@
917
import org.springframework.context.annotation.Configuration;
1018
import org.springframework.context.annotation.Lazy;
1119

20+
import javax.validation.ConstraintViolationException;
1221
import javax.validation.Validator;
22+
import java.util.Optional;
1323

1424
@Configuration
15-
@ConditionalOnClass(Validator.class)
25+
@ConditionalOnClass({Validator.class})
26+
1627
@EnableConfigurationProperties(GRpcValidationProperties.class)
1728
public class GRpcValidationConfiguration {
29+
30+
31+
1832
@Bean
1933
@ConditionalOnBean(Validator.class)
2034
@GRpcGlobalInterceptor
21-
public ValidatingInterceptor validatingInterceptor(@Lazy Validator validator,GRpcValidationProperties validationProperties){
22-
return new ValidatingInterceptor(validator)
35+
public ValidatingInterceptor validatingInterceptor(@Lazy Validator validator, GRpcValidationProperties validationProperties,@Lazy FailureHandlingSupport failureHandlingSupport){
36+
return new ValidatingInterceptor(validator,failureHandlingSupport)
2337
.order(validationProperties.getInterceptorOrder());
2438
}
39+
40+
@ConditionalOnMissingErrorHandler(ConstraintViolationException.class)
41+
@Configuration
42+
static class DefaultValidationHandlerConfig{
43+
@GRpcServiceAdvice
44+
@Slf4j
45+
public static class DefaultValidationErrorHandler extends ErrorHandlerAdapter {
46+
47+
public DefaultValidationErrorHandler(Optional<GRpcErrorHandler> errorHandler) {
48+
super(errorHandler);
49+
}
50+
51+
@GRpcExceptionHandler
52+
public Status handle(ConstraintViolationException e, GRpcExceptionScope scope){
53+
return handle(e,scope.getHintAs(Status.class).get(),scope);
54+
}
55+
}
56+
57+
}
58+
59+
60+
2561
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package org.lognet.springboot.grpc.autoconfigure;
2+
3+
import org.lognet.springboot.grpc.recovery.GRpcExceptionHandler;
4+
import org.lognet.springboot.grpc.recovery.GRpcServiceAdvice;
5+
import org.lognet.springboot.grpc.recovery.HandlerMethod;
6+
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
7+
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
8+
import org.springframework.context.annotation.ConditionContext;
9+
import org.springframework.core.MethodIntrospector;
10+
import org.springframework.core.annotation.AnnotatedElementUtils;
11+
import org.springframework.core.type.AnnotatedTypeMetadata;
12+
import org.springframework.util.ReflectionUtils;
13+
14+
import java.lang.reflect.Method;
15+
import java.util.Optional;
16+
17+
public class OnMissingErrorHandlerCondition extends SpringBootCondition {
18+
19+
20+
@Override
21+
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
22+
Class<? extends Throwable> exc = (Class<? extends Throwable>) metadata
23+
.getAnnotationAttributes(ConditionalOnMissingErrorHandler.class.getName())
24+
.get("value");
25+
26+
ReflectionUtils.MethodFilter f = method -> AnnotatedElementUtils.hasAnnotation(method, GRpcExceptionHandler.class);
27+
for(String adviceBeanName:context.getBeanFactory().getBeanNamesForAnnotation(GRpcServiceAdvice.class)){
28+
final String beanClassName = context.getBeanFactory().getBeanDefinition(adviceBeanName)
29+
.getBeanClassName();
30+
31+
try {
32+
for (Method method : MethodIntrospector.selectMethods(Class.forName(beanClassName), f)) {
33+
final Optional<Class<? extends Throwable>> handledException = HandlerMethod.getHandledException(method, false);
34+
if(handledException.isPresent() && handledException.get().isAssignableFrom(exc)){
35+
return ConditionOutcome.noMatch(String.format("Found %s handler at %s.%s",
36+
handledException.get().getName(),
37+
beanClassName,
38+
method.getName()
39+
));
40+
}
41+
}
42+
} catch (ClassNotFoundException e) {
43+
throw new IllegalStateException(e);
44+
}
45+
};
46+
47+
return ConditionOutcome.match();
48+
}
49+
}

0 commit comments

Comments
 (0)