diff --git a/spring-grpc-core/src/main/java/org/springframework/grpc/server/exception/ReactiveStubBeanDefinitionRegistrar.java b/spring-grpc-core/src/main/java/org/springframework/grpc/server/exception/ReactiveStubBeanDefinitionRegistrar.java index 06794d8..81cc438 100644 --- a/spring-grpc-core/src/main/java/org/springframework/grpc/server/exception/ReactiveStubBeanDefinitionRegistrar.java +++ b/spring-grpc-core/src/main/java/org/springframework/grpc/server/exception/ReactiveStubBeanDefinitionRegistrar.java @@ -92,13 +92,14 @@ public class ReactiveStubBeanDefinitionRegistrar implements ImportBeanDefinition for (String name : factory.getBeanNamesForType(BindableService.class)) { BeanDefinition service = factory.getBeanDefinition(name); Class type = factory.getType(name); - if (type != null && ReflectionUtils.findMethod(type, "onErrorMap", Throwable.class) != null) { - if (service instanceof AbstractBeanDefinition root) { + if (type != null) { + Method method = ReflectionUtils.findMethod(type, "onErrorMap", Throwable.class); + if (method != null && method.getDeclaringClass() != type + && service instanceof AbstractBeanDefinition root) { ReplaceOverride override = new ReplaceOverride("onErrorMap", BEAN_NAME); - // You need this in an AOT build (but the interceptor still isn't - // used - // at runtime with AOT - // https://github.com/spring-projects/spring-framework/issues/34642) + // You need this in an AOT build (but the interceptor still + // isn't used at runtime with AOT + // spring-projects/spring-framework#34642) override.addTypeIdentifier("Throwable"); root.getMethodOverrides().addOverride(override); } diff --git a/spring-grpc-core/src/test/java/org/springframework/grpc/server/exception/ReactiveStubBeanDefinitionRegistrarTests.java b/spring-grpc-core/src/test/java/org/springframework/grpc/server/exception/ReactiveStubBeanDefinitionRegistrarTests.java new file mode 100644 index 0000000..6e26487 --- /dev/null +++ b/spring-grpc-core/src/test/java/org/springframework/grpc/server/exception/ReactiveStubBeanDefinitionRegistrarTests.java @@ -0,0 +1,106 @@ +/* + * Copyright 2024-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.grpc.server.exception; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.context.support.StaticApplicationContext; +import org.springframework.core.type.AnnotationMetadata; + +import io.grpc.BindableService; +import io.grpc.ServerServiceDefinition; + +public class ReactiveStubBeanDefinitionRegistrarTests { + + private static final String BEAN_NAME = ReactiveStubBeanDefinitionRegistrar.ReactiveStubBeanFactoryPostProcessor.BEAN_NAME; + + private StaticApplicationContext registry = new StaticApplicationContext(); + + private ReactiveStubBeanDefinitionRegistrar registrar = new ReactiveStubBeanDefinitionRegistrar(); + + private AnnotationMetadata metadata = null; + + private ReactiveStubBeanDefinitionRegistrar.ReactiveStubBeanFactoryPostProcessor processor; + + @BeforeEach + void setup() { + registry.registerSingleton("exceptionHandler", GrpcExceptionHandler.class); + registrar.registerBeanDefinitions(metadata, registry); + processor = (ReactiveStubBeanDefinitionRegistrar.ReactiveStubBeanFactoryPostProcessor) registry + .getBean(BEAN_NAME); + processor.setApplicationContext(registry); + } + + @Test + void defaultDoNothing() { + assertThat(registry.containsBeanDefinition(BEAN_NAME)).isTrue(); + } + + @Test + void postProcessNonReactiveBean() { + registry.registerBean("service", MyService.class); + processor.postProcessBeanFactory(registry.getDefaultListableBeanFactory()); + AbstractBeanDefinition bean = (AbstractBeanDefinition) registry.getBeanDefinition("service"); + assertThat(bean.hasMethodOverrides()).isFalse(); + } + + @Test + void postProcessReactiveBean() { + registry.registerBean("service", MyReactiveService.class); + processor.postProcessBeanFactory(registry.getDefaultListableBeanFactory()); + AbstractBeanDefinition bean = (AbstractBeanDefinition) registry.getBeanDefinition("service"); + assertThat(bean.hasMethodOverrides()).isTrue(); + } + + @Test + void postProcessReactiveBeanWithOnErrorMap() { + registry.registerBean("service", MyReactiveStub.class); + processor.postProcessBeanFactory(registry.getDefaultListableBeanFactory()); + AbstractBeanDefinition bean = (AbstractBeanDefinition) registry.getBeanDefinition("service"); + assertThat(bean.hasMethodOverrides()).isFalse(); + } + + static class MyService implements BindableService { + + @Override + public ServerServiceDefinition bindService() { + return null; + } + + } + + static class MyReactiveService extends MyReactiveStub { + + } + + static class MyReactiveStub implements BindableService { + + @Override + public ServerServiceDefinition bindService() { + return null; + } + + protected Throwable onErrorMap(Throwable throwable) { + return throwable; + } + + } + +}