Skip to content

Commit

Permalink
deprecate GRpcErrorHandler in favor of GRpcExceptionHandler and GRpcS…
Browse files Browse the repository at this point in the history
…erviceAdvice
  • Loading branch information
Alexander Furer committed Oct 14, 2021
1 parent 4efb349 commit 19fc751
Show file tree
Hide file tree
Showing 22 changed files with 520 additions and 198 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import io.grpc.StatusRuntimeException;
import io.grpc.examples.SecuredGreeterGrpc;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.lognet.springboot.grpc.GrpcServerTestBase;
Expand All @@ -28,7 +27,6 @@

import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertNotNull;

import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE;
Expand All @@ -48,8 +46,6 @@ public class DemoGrpcSecurityConfig extends GrpcSecurityConfigurerAdapter {

@Override
public void configure(GrpcSecurity builder) throws Exception {


builder.authorizeRequests()
.withSecuredAnnotation()
.authenticationSchemeSelector(scheme ->
Expand All @@ -64,7 +60,6 @@ public void configure(GrpcSecurity builder) throws Exception {
.authenticationProvider(new TestingAuthenticationProvider());
}


}

}
Expand All @@ -88,7 +83,7 @@ public void customSchemeAccessDeniedTest() {

}

private String invoke(String userName, String authority) {
private String invoke(String userName, String authority) {
AuthCallCredentials callCredentials = new AuthCallCredentials(
AuthHeader.builder().authScheme(MY_CUSTOM_SCHEME_NAME).tokenSupplier(() ->
ByteBuffer.wrap(String.format("%s#%s", userName, authority).getBytes()))
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.lognet.springboot.grpc;

import io.grpc.Metadata;
import io.grpc.ServerCall;
import io.grpc.Status;
import org.lognet.springboot.grpc.recovery.GRpcExceptionHandlerMethodResolver;
import org.lognet.springboot.grpc.recovery.GRpcExceptionScope;
import org.lognet.springboot.grpc.recovery.GRpcRuntimeExceptionWrapper;
import org.lognet.springboot.grpc.recovery.HandlerMethod;

import java.util.Optional;
import java.util.function.Consumer;

public class FailureHandlingSupport {

private final GRpcExceptionHandlerMethodResolver methodResolver;

public FailureHandlingSupport(GRpcExceptionHandlerMethodResolver methodResolver) {
this.methodResolver = methodResolver;
}

public void closeCall(RuntimeException e, ServerCall<?, ?> call, Metadata headers, Consumer<GRpcExceptionScope.GRpcExceptionScopeBuilder> customizer) throws RuntimeException {


final Optional<HandlerMethod> handlerMethod = methodResolver.resolveMethodByThrowable(call.getMethodDescriptor().getServiceName(), e);
if (handlerMethod.isPresent()) {
final GRpcExceptionScope.GRpcExceptionScopeBuilder exceptionScopeBuilder = GRpcExceptionScope.builder()
.callHeaders(headers)
.methodCallAttributes(call.getAttributes())
.methodDescriptor(call.getMethodDescriptor())
.hint(GRpcRuntimeExceptionWrapper.getHint(e));
customizer.accept(exceptionScopeBuilder);

final GRpcExceptionScope excScope = exceptionScopeBuilder.build();

final HandlerMethod handler = handlerMethod.get();


Status statusToSend = Status.INTERNAL;
try {
statusToSend = handler.invoke(GRpcRuntimeExceptionWrapper.unwrap(e),excScope );
}catch (Exception handlerException){

org.slf4j.LoggerFactory.getLogger(this.getClass())
.error("Caught exception while executing handler method {}, returning {} status.",
handler.getMethod(),
statusToSend,
handlerException);

}
call.close(statusToSend, excScope.getResponseHeaders());


} else {
call.close(Status.INTERNAL,new Metadata());
}

}



}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
import io.grpc.Status;
import lombok.extern.slf4j.Slf4j;



/**
*
* Please use {@link org.lognet.springboot.grpc.recovery.GRpcExceptionHandler} instead
*/
@Deprecated
@Slf4j
public class GRpcErrorHandler {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.lognet.springboot.grpc;

import io.grpc.ForwardingServerCallListener;
import io.grpc.ServerCall;


public class MessageBlockingServerCallListener<R> extends ForwardingServerCallListener.SimpleForwardingServerCallListener<R> {


private volatile boolean messageBlocked = false;


public MessageBlockingServerCallListener(ServerCall.Listener<R> delegate) {
super(delegate);
}

@Override
public void onHalfClose() {
// If the message was blocked, downstream never had a chance to react to it. Hence, the half-close signal would look like
// an error to them. So we do not propagate the signal in that case.
if (!messageBlocked) {
super.onHalfClose();
}
}

protected void blockMessage() {
messageBlocked = true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.lognet.springboot.grpc.autoconfigure;


import org.springframework.context.annotation.Conditional;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.TYPE , ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnMissingErrorHandlerCondition.class)
public @interface ConditionalOnMissingErrorHandler {
Class<? extends Throwable> value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.grpc.ServerBuilder;
import io.grpc.inprocess.InProcessServerBuilder;
import org.lognet.springboot.grpc.FailureHandlingSupport;
import org.lognet.springboot.grpc.GRpcGlobalInterceptor;
import org.lognet.springboot.grpc.GRpcServerBuilderConfigurer;
import org.lognet.springboot.grpc.GRpcServerRunner;
Expand Down Expand Up @@ -43,7 +44,6 @@
@ConditionalOnBean(annotation = GRpcService.class)
@EnableConfigurationProperties({GRpcServerProperties.class})
@Import({GRpcValidationConfiguration.class,

NettyServerBuilderSelector.class,
DefaultHealthStatusService.class
})
Expand All @@ -59,26 +59,33 @@ public GRpcServerRunner grpcServerRunner(@Qualifier("grpcInternalConfigurator")
return new GRpcServerRunner(configurator, serverBuilder);
}


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


@Bean
public GRpcServicesRegistry grpcServicesRegistry() {
return new GRpcServicesRegistry();
}

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

final GRpcExceptionHandlerMethodResolver methodResolver = new GRpcExceptionHandlerMethodResolver(gRpcServicesRegistry, advices);
return new GRpcExceptionHandlerInterceptor(methodResolver);
@Bean
public FailureHandlingSupport failureHandlingSupport(GRpcExceptionHandlerMethodResolver methodResolver){
return new FailureHandlingSupport(methodResolver);
}

@Bean
@GRpcGlobalInterceptor
public GRpcExceptionHandlerInterceptor exceptionHandlerInterceptor(FailureHandlingSupport failureHandlingSupport,
GRpcExceptionHandlerMethodResolver methodResolver) {
return new GRpcExceptionHandlerInterceptor(methodResolver,failureHandlingSupport);
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
package org.lognet.springboot.grpc.autoconfigure;

import io.grpc.Status;
import lombok.extern.slf4j.Slf4j;
import org.lognet.springboot.grpc.FailureHandlingSupport;
import org.lognet.springboot.grpc.GRpcErrorHandler;
import org.lognet.springboot.grpc.GRpcGlobalInterceptor;
import org.lognet.springboot.grpc.recovery.ErrorHandlerAdapter;
import org.lognet.springboot.grpc.recovery.GRpcExceptionHandler;
import org.lognet.springboot.grpc.recovery.GRpcExceptionScope;
import org.lognet.springboot.grpc.recovery.GRpcServiceAdvice;
import org.lognet.springboot.grpc.validation.ValidatingInterceptor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
Expand All @@ -9,17 +17,45 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import java.util.Optional;

@Configuration
@ConditionalOnClass(Validator.class)
@ConditionalOnClass({Validator.class})

@EnableConfigurationProperties(GRpcValidationProperties.class)
public class GRpcValidationConfiguration {



@Bean
@ConditionalOnBean(Validator.class)
@GRpcGlobalInterceptor
public ValidatingInterceptor validatingInterceptor(@Lazy Validator validator,GRpcValidationProperties validationProperties){
return new ValidatingInterceptor(validator)
public ValidatingInterceptor validatingInterceptor(@Lazy Validator validator, GRpcValidationProperties validationProperties,@Lazy FailureHandlingSupport failureHandlingSupport){
return new ValidatingInterceptor(validator,failureHandlingSupport)
.order(validationProperties.getInterceptorOrder());
}

@ConditionalOnMissingErrorHandler(ConstraintViolationException.class)
@Configuration
static class DefaultValidationHandlerConfig{
@GRpcServiceAdvice
@Slf4j
public static class DefaultValidationErrorHandler extends ErrorHandlerAdapter {

public DefaultValidationErrorHandler(Optional<GRpcErrorHandler> errorHandler) {
super(errorHandler);
}

@GRpcExceptionHandler
public Status handle(ConstraintViolationException e, GRpcExceptionScope scope){
return handle(e,scope.getHintAs(Status.class).get(),scope);
}
}

}



}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.lognet.springboot.grpc.autoconfigure;

import org.lognet.springboot.grpc.recovery.GRpcExceptionHandler;
import org.lognet.springboot.grpc.recovery.GRpcServiceAdvice;
import org.lognet.springboot.grpc.recovery.HandlerMethod;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.util.Optional;

public class OnMissingErrorHandlerCondition extends SpringBootCondition {


@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
Class<? extends Throwable> exc = (Class<? extends Throwable>) metadata
.getAnnotationAttributes(ConditionalOnMissingErrorHandler.class.getName())
.get("value");

ReflectionUtils.MethodFilter f = method -> AnnotatedElementUtils.hasAnnotation(method, GRpcExceptionHandler.class);
for(String adviceBeanName:context.getBeanFactory().getBeanNamesForAnnotation(GRpcServiceAdvice.class)){
final String beanClassName = context.getBeanFactory().getBeanDefinition(adviceBeanName)
.getBeanClassName();

try {
for (Method method : MethodIntrospector.selectMethods(Class.forName(beanClassName), f)) {
final Optional<Class<? extends Throwable>> handledException = HandlerMethod.getHandledException(method, false);
if(handledException.isPresent() && handledException.get().isAssignableFrom(exc)){
return ConditionOutcome.noMatch(String.format("Found %s handler at %s.%s",
handledException.get().getName(),
beanClassName,
method.getName()
));
}
}
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e);
}
};

return ConditionOutcome.match();
}
}

0 comments on commit 19fc751

Please sign in to comment.