Skip to content

Commit 7e43d7b

Browse files
committed
Merge pull request #39611 from BenchmarkingBuffalo
* pr/39611: Polish "Configure suitable TaskExecutor for WebSocket" Configure suitable TaskExecutor for WebSocket Closes gh-39611
2 parents 3dd3fc8 + 9110b12 commit 7e43d7b

File tree

2 files changed

+68
-4
lines changed

2 files changed

+68
-4
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketMessagingAutoConfiguration.java

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
1717
package org.springframework.boot.autoconfigure.websocket.servlet;
1818

1919
import java.util.List;
20+
import java.util.Map;
2021

2122
import com.fasterxml.jackson.databind.ObjectMapper;
2223

@@ -28,14 +29,17 @@
2829
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
2930
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
3031
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
32+
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
3133
import org.springframework.context.annotation.Bean;
3234
import org.springframework.context.annotation.Configuration;
35+
import org.springframework.core.task.AsyncTaskExecutor;
3336
import org.springframework.messaging.converter.ByteArrayMessageConverter;
3437
import org.springframework.messaging.converter.DefaultContentTypeResolver;
3538
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
3639
import org.springframework.messaging.converter.MessageConverter;
3740
import org.springframework.messaging.converter.StringMessageConverter;
3841
import org.springframework.messaging.simp.config.AbstractMessageBrokerConfiguration;
42+
import org.springframework.messaging.simp.config.ChannelRegistration;
3943
import org.springframework.util.MimeTypeUtils;
4044
import org.springframework.web.socket.config.annotation.DelegatingWebSocketMessageBrokerConfiguration;
4145
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@@ -44,6 +48,8 @@
4448
* {@link EnableAutoConfiguration Auto-configuration} for WebSocket-based messaging.
4549
*
4650
* @author Andy Wilkinson
51+
* @author Lasse Wulff
52+
* @author Moritz Halbritter
4753
* @since 1.3.0
4854
*/
4955
@AutoConfiguration(after = JacksonAutoConfiguration.class)
@@ -58,8 +64,19 @@ static class WebSocketMessageConverterConfiguration implements WebSocketMessageB
5864

5965
private final ObjectMapper objectMapper;
6066

61-
WebSocketMessageConverterConfiguration(ObjectMapper objectMapper) {
67+
private final AsyncTaskExecutor executor;
68+
69+
WebSocketMessageConverterConfiguration(ObjectMapper objectMapper,
70+
Map<String, AsyncTaskExecutor> taskExecutors) {
6271
this.objectMapper = objectMapper;
72+
this.executor = determineAsyncTaskExecutor(taskExecutors);
73+
}
74+
75+
private static AsyncTaskExecutor determineAsyncTaskExecutor(Map<String, AsyncTaskExecutor> taskExecutors) {
76+
if (taskExecutors.size() == 1) {
77+
return taskExecutors.values().iterator().next();
78+
}
79+
return taskExecutors.get(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME);
6380
}
6481

6582
@Override
@@ -74,6 +91,20 @@ public boolean configureMessageConverters(List<MessageConverter> messageConverte
7491
return false;
7592
}
7693

94+
@Override
95+
public void configureClientInboundChannel(ChannelRegistration registration) {
96+
if (this.executor != null) {
97+
registration.executor(this.executor);
98+
}
99+
}
100+
101+
@Override
102+
public void configureClientOutboundChannel(ChannelRegistration registration) {
103+
if (this.executor != null) {
104+
registration.executor(this.executor);
105+
}
106+
}
107+
77108
@Bean
78109
static LazyInitializationExcludeFilter eagerStompWebSocketHandlerMapping() {
79110
return (name, definition, type) -> name.equals("stompWebSocketHandlerMapping");

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketMessagingAutoConfigurationTests.java

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,8 +19,10 @@
1919
import java.lang.reflect.Type;
2020
import java.util.ArrayList;
2121
import java.util.Arrays;
22+
import java.util.Collections;
2223
import java.util.Iterator;
2324
import java.util.List;
25+
import java.util.Map;
2426
import java.util.concurrent.CountDownLatch;
2527
import java.util.concurrent.TimeUnit;
2628
import java.util.concurrent.atomic.AtomicReference;
@@ -35,6 +37,7 @@
3537
import org.springframework.boot.LazyInitializationBeanFactoryPostProcessor;
3638
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
3739
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
40+
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
3841
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
3942
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
4043
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -43,17 +46,22 @@
4346
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
4447
import org.springframework.context.annotation.Bean;
4548
import org.springframework.context.annotation.Configuration;
49+
import org.springframework.core.task.AsyncTaskExecutor;
50+
import org.springframework.core.task.SimpleAsyncTaskExecutor;
51+
import org.springframework.core.task.TaskExecutor;
4652
import org.springframework.messaging.converter.CompositeMessageConverter;
4753
import org.springframework.messaging.converter.MessageConverter;
4854
import org.springframework.messaging.converter.SimpleMessageConverter;
4955
import org.springframework.messaging.simp.annotation.SubscribeMapping;
56+
import org.springframework.messaging.simp.config.ChannelRegistration;
5057
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
5158
import org.springframework.messaging.simp.stomp.StompCommand;
5259
import org.springframework.messaging.simp.stomp.StompFrameHandler;
5360
import org.springframework.messaging.simp.stomp.StompHeaders;
5461
import org.springframework.messaging.simp.stomp.StompSession;
5562
import org.springframework.messaging.simp.stomp.StompSessionHandler;
5663
import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;
64+
import org.springframework.security.util.FieldUtils;
5765
import org.springframework.stereotype.Controller;
5866
import org.springframework.web.client.RestTemplate;
5967
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
@@ -75,6 +83,7 @@
7583
* Tests for {@link WebSocketMessagingAutoConfiguration}.
7684
*
7785
* @author Andy Wilkinson
86+
* @author Lasse Wulff
7887
*/
7988
class WebSocketMessagingAutoConfigurationTests {
8089

@@ -129,10 +138,34 @@ void customizedConverterTypesMatchDefaultConverterTypes() {
129138
}
130139
}
131140

141+
@Test
142+
void predefinedThreadExecutorIsSelectedForInboundChannel() throws Throwable {
143+
AsyncTaskExecutor expectedExecutor = new SimpleAsyncTaskExecutor();
144+
ChannelRegistration registration = new ChannelRegistration();
145+
WebSocketMessagingAutoConfiguration.WebSocketMessageConverterConfiguration configuration = new WebSocketMessagingAutoConfiguration.WebSocketMessageConverterConfiguration(
146+
new ObjectMapper(),
147+
Map.of(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME, expectedExecutor));
148+
configuration.configureClientInboundChannel(registration);
149+
TaskExecutor executor = (TaskExecutor) FieldUtils.getFieldValue(registration, "executor");
150+
assertThat(executor).isEqualTo(expectedExecutor);
151+
}
152+
153+
@Test
154+
void predefinedThreadExecutorIsSelectedForOutboundChannel() throws Throwable {
155+
AsyncTaskExecutor expectedExecutor = new SimpleAsyncTaskExecutor();
156+
ChannelRegistration registration = new ChannelRegistration();
157+
WebSocketMessagingAutoConfiguration.WebSocketMessageConverterConfiguration configuration = new WebSocketMessagingAutoConfiguration.WebSocketMessageConverterConfiguration(
158+
new ObjectMapper(),
159+
Map.of(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME, expectedExecutor));
160+
configuration.configureClientOutboundChannel(registration);
161+
TaskExecutor executor = (TaskExecutor) FieldUtils.getFieldValue(registration, "executor");
162+
assertThat(executor).isEqualTo(expectedExecutor);
163+
}
164+
132165
private List<MessageConverter> getCustomizedConverters() {
133166
List<MessageConverter> customizedConverters = new ArrayList<>();
134167
WebSocketMessagingAutoConfiguration.WebSocketMessageConverterConfiguration configuration = new WebSocketMessagingAutoConfiguration.WebSocketMessageConverterConfiguration(
135-
new ObjectMapper());
168+
new ObjectMapper(), Collections.emptyMap());
136169
configuration.configureMessageConverters(customizedConverters);
137170
return customizedConverters;
138171
}

0 commit comments

Comments
 (0)