Skip to content

Commit c5722ea

Browse files
committed
Support @ControllerAdvice in RSocket messaging auto-configuration
See gh-45360 Signed-off-by: Dmitry Sulman <[email protected]>
1 parent 862db41 commit c5722ea

File tree

2 files changed

+94
-1
lines changed

2 files changed

+94
-1
lines changed

module/spring-boot-rsocket/src/main/java/org/springframework/boot/rsocket/autoconfigure/RSocketMessagingAutoConfiguration.java

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,27 @@
1717
package org.springframework.boot.rsocket.autoconfigure;
1818

1919
import io.rsocket.transport.netty.server.TcpServerTransport;
20+
import org.jspecify.annotations.Nullable;
2021

2122
import org.springframework.beans.factory.ObjectProvider;
2223
import org.springframework.boot.autoconfigure.AutoConfiguration;
2324
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
2425
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2526
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
27+
import org.springframework.context.ApplicationContext;
2628
import org.springframework.context.annotation.Bean;
29+
import org.springframework.messaging.handler.MessagingAdviceBean;
2730
import org.springframework.messaging.rsocket.RSocketRequester;
2831
import org.springframework.messaging.rsocket.RSocketStrategies;
2932
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
33+
import org.springframework.web.method.ControllerAdviceBean;
3034

3135
/**
3236
* {@link EnableAutoConfiguration Auto-configuration} for Spring RSocket support in Spring
3337
* Messaging.
3438
*
3539
* @author Brian Clozel
40+
* @author Dmitry Sulman
3641
* @since 4.0.0
3742
*/
3843
@AutoConfiguration(after = RSocketStrategiesAutoConfiguration.class)
@@ -42,11 +47,44 @@ public final class RSocketMessagingAutoConfiguration {
4247
@Bean
4348
@ConditionalOnMissingBean
4449
RSocketMessageHandler messageHandler(RSocketStrategies rSocketStrategies,
45-
ObjectProvider<RSocketMessageHandlerCustomizer> customizers) {
50+
ObjectProvider<RSocketMessageHandlerCustomizer> customizers, ApplicationContext context) {
4651
RSocketMessageHandler messageHandler = new RSocketMessageHandler();
4752
messageHandler.setRSocketStrategies(rSocketStrategies);
4853
customizers.orderedStream().forEach((customizer) -> customizer.customize(messageHandler));
54+
ControllerAdviceBean.findAnnotatedBeans(context)
55+
.forEach((controllerAdviceBean) -> messageHandler
56+
.registerMessagingAdvice(new ControllerAdviceBeanWrapper(controllerAdviceBean)));
4957
return messageHandler;
5058
}
5159

60+
private static final class ControllerAdviceBeanWrapper implements MessagingAdviceBean {
61+
62+
private final ControllerAdviceBean adviceBean;
63+
64+
private ControllerAdviceBeanWrapper(ControllerAdviceBean adviceBean) {
65+
this.adviceBean = adviceBean;
66+
}
67+
68+
@Override
69+
public @Nullable Class<?> getBeanType() {
70+
return this.adviceBean.getBeanType();
71+
}
72+
73+
@Override
74+
public Object resolveBean() {
75+
return this.adviceBean.resolveBean();
76+
}
77+
78+
@Override
79+
public boolean isApplicableToBeanType(Class<?> beanType) {
80+
return this.adviceBean.isApplicableToBeanType(beanType);
81+
}
82+
83+
@Override
84+
public int getOrder() {
85+
return this.adviceBean.getOrder();
86+
}
87+
88+
}
89+
5290
}

module/spring-boot-rsocket/src/test/java/org/springframework/boot/rsocket/autoconfigure/RSocketMessagingAutoConfigurationTests.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,30 @@
1616

1717
package org.springframework.boot.rsocket.autoconfigure;
1818

19+
import io.rsocket.frame.FrameType;
1920
import org.junit.jupiter.api.Test;
21+
import reactor.core.publisher.Mono;
22+
import reactor.test.StepVerifier;
2023

2124
import org.springframework.boot.autoconfigure.AutoConfigurations;
2225
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
2326
import org.springframework.context.annotation.Bean;
2427
import org.springframework.context.annotation.Configuration;
2528
import org.springframework.core.codec.CharSequenceEncoder;
2629
import org.springframework.core.codec.StringDecoder;
30+
import org.springframework.messaging.Message;
31+
import org.springframework.messaging.handler.DestinationPatternsMessageCondition;
32+
import org.springframework.messaging.handler.annotation.MessageExceptionHandler;
33+
import org.springframework.messaging.handler.annotation.MessageMapping;
2734
import org.springframework.messaging.rsocket.RSocketStrategies;
35+
import org.springframework.messaging.rsocket.annotation.support.RSocketFrameTypeMessageCondition;
2836
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
37+
import org.springframework.messaging.support.MessageBuilder;
38+
import org.springframework.messaging.support.MessageHeaderAccessor;
39+
import org.springframework.stereotype.Controller;
2940
import org.springframework.util.MimeType;
41+
import org.springframework.util.RouteMatcher;
42+
import org.springframework.web.bind.annotation.ControllerAdvice;
3043

3144
import static org.assertj.core.api.Assertions.assertThat;
3245

@@ -72,6 +85,22 @@ void shouldApplyMessageHandlerCustomizers() {
7285
});
7386
}
7487

88+
@Test
89+
void shouldRegisterControllerAdvice() {
90+
this.contextRunner.withBean(TestControllerAdvice.class).withBean(TestController.class).run((context) -> {
91+
RSocketMessageHandler handler = context.getBean(RSocketMessageHandler.class);
92+
TestControllerAdvice controllerAdvice = context.getBean(TestControllerAdvice.class);
93+
94+
MessageHeaderAccessor headers = new MessageHeaderAccessor();
95+
RouteMatcher.Route route = handler.getRouteMatcher().parseRoute("exception");
96+
headers.setHeader(DestinationPatternsMessageCondition.LOOKUP_DESTINATION_HEADER, route);
97+
headers.setHeader(RSocketFrameTypeMessageCondition.FRAME_TYPE_HEADER, FrameType.REQUEST_FNF);
98+
Message<?> message = MessageBuilder.createMessage(Mono.empty(), headers.getMessageHeaders());
99+
StepVerifier.create(handler.handleMessage(message)).expectComplete().verify();
100+
assertThat(controllerAdvice.isExceptionHandled()).isTrue();
101+
});
102+
}
103+
75104
@Configuration(proxyBeanMethods = false)
76105
static class BaseConfiguration {
77106

@@ -111,4 +140,30 @@ RSocketMessageHandlerCustomizer customizer() {
111140

112141
}
113142

143+
@Controller
144+
static final class TestController {
145+
146+
@MessageMapping("exception")
147+
void handleWithSimulatedException() {
148+
throw new IllegalStateException("simulated exception");
149+
}
150+
151+
}
152+
153+
@ControllerAdvice
154+
static final class TestControllerAdvice {
155+
156+
boolean exceptionHandled;
157+
158+
boolean isExceptionHandled() {
159+
return this.exceptionHandled;
160+
}
161+
162+
@MessageExceptionHandler
163+
void handleException(IllegalStateException ex) {
164+
this.exceptionHandled = true;
165+
}
166+
167+
}
168+
114169
}

0 commit comments

Comments
 (0)