Skip to content

Commit 4becce1

Browse files
committed
Consolidate default WebMvc executor log warnings
Closes gh-30902
1 parent 8a283e3 commit 4becce1

File tree

3 files changed

+34
-57
lines changed

3 files changed

+34
-57
lines changed

spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030

3131
import org.springframework.core.task.AsyncTaskExecutor;
3232
import org.springframework.core.task.SimpleAsyncTaskExecutor;
33-
import org.springframework.core.task.SyncTaskExecutor;
3433
import org.springframework.lang.Nullable;
3534
import org.springframework.util.Assert;
3635
import org.springframework.web.context.request.RequestAttributes;
@@ -74,8 +73,6 @@ public final class WebAsyncManager {
7473
private static final DeferredResultProcessingInterceptor timeoutDeferredResultInterceptor =
7574
new TimeoutDeferredResultProcessingInterceptor();
7675

77-
private static Boolean taskExecutorWarning = true;
78-
7976

8077
@Nullable
8178
private AsyncWebRequest asyncWebRequest;
@@ -295,9 +292,6 @@ public void startCallableProcessing(final WebAsyncTask<?> webAsyncTask, Object..
295292
if (executor != null) {
296293
this.taskExecutor = executor;
297294
}
298-
else {
299-
logExecutorWarning();
300-
}
301295

302296
List<CallableProcessingInterceptor> interceptors = new ArrayList<>();
303297
interceptors.add(webAsyncTask.getInterceptor());
@@ -357,26 +351,6 @@ public void startCallableProcessing(final WebAsyncTask<?> webAsyncTask, Object..
357351
}
358352
}
359353

360-
private void logExecutorWarning() {
361-
if (taskExecutorWarning && logger.isWarnEnabled()) {
362-
synchronized (DEFAULT_TASK_EXECUTOR) {
363-
AsyncTaskExecutor executor = this.taskExecutor;
364-
if (taskExecutorWarning &&
365-
(executor instanceof SimpleAsyncTaskExecutor || executor instanceof SyncTaskExecutor)) {
366-
String executorTypeName = executor.getClass().getSimpleName();
367-
logger.warn("\n!!!\n" +
368-
"An Executor is required to handle java.util.concurrent.Callable return values.\n" +
369-
"Please, configure a TaskExecutor in the MVC config under \"async support\".\n" +
370-
"The " + executorTypeName + " currently in use is not suitable under load.\n" +
371-
"-------------------------------\n" +
372-
"Request URI: '" + formatRequestUri() + "'\n" +
373-
"!!!");
374-
taskExecutorWarning = false;
375-
}
376-
}
377-
}
378-
}
379-
380354
private String formatRequestUri() {
381355
HttpServletRequest request = this.asyncWebRequest.getNativeRequest(HttpServletRequest.class);
382356
return request != null ? request.getRequestURI() : "servlet container";

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ReactiveTypeHandler.java

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import org.springframework.core.ReactiveAdapter;
3939
import org.springframework.core.ReactiveAdapterRegistry;
4040
import org.springframework.core.ResolvableType;
41-
import org.springframework.core.task.SimpleAsyncTaskExecutor;
4241
import org.springframework.core.task.SyncTaskExecutor;
4342
import org.springframework.core.task.TaskExecutor;
4443
import org.springframework.http.MediaType;
@@ -91,8 +90,6 @@ class ReactiveTypeHandler {
9190

9291
private final ContentNegotiationManager contentNegotiationManager;
9392

94-
private boolean taskExecutorWarning;
95-
9693

9794
public ReactiveTypeHandler() {
9895
this(ReactiveAdapterRegistry.getSharedInstance(), new SyncTaskExecutor(), new ContentNegotiationManager());
@@ -105,9 +102,6 @@ public ReactiveTypeHandler() {
105102
this.adapterRegistry = registry;
106103
this.taskExecutor = executor;
107104
this.contentNegotiationManager = manager;
108-
109-
this.taskExecutorWarning =
110-
(executor instanceof SimpleAsyncTaskExecutor || executor instanceof SyncTaskExecutor);
111105
}
112106

113107

@@ -147,20 +141,17 @@ public ResponseBodyEmitter handleValue(Object returnValue, MethodParameter retur
147141
if (adapter.isMultiValue()) {
148142
if (mediaTypes.stream().anyMatch(MediaType.TEXT_EVENT_STREAM::includes) ||
149143
ServerSentEvent.class.isAssignableFrom(elementClass)) {
150-
logExecutorWarning(returnType);
151144
SseEmitter emitter = new SseEmitter(STREAMING_TIMEOUT_VALUE);
152145
new SseEmitterSubscriber(emitter, this.taskExecutor).connect(adapter, returnValue);
153146
return emitter;
154147
}
155148
if (CharSequence.class.isAssignableFrom(elementClass)) {
156-
logExecutorWarning(returnType);
157149
ResponseBodyEmitter emitter = getEmitter(mediaType.orElse(MediaType.TEXT_PLAIN));
158150
new TextEmitterSubscriber(emitter, this.taskExecutor).connect(adapter, returnValue);
159151
return emitter;
160152
}
161153
MediaType streamingResponseType = findConcreteStreamingMediaType(mediaTypes);
162154
if (streamingResponseType != null) {
163-
logExecutorWarning(returnType);
164155
ResponseBodyEmitter emitter = getEmitter(streamingResponseType);
165156
new JsonEmitterSubscriber(emitter, this.taskExecutor).connect(adapter, returnValue);
166157
return emitter;
@@ -234,27 +225,6 @@ protected void extendResponse(ServerHttpResponse outputMessage) {
234225
};
235226
}
236227

237-
@SuppressWarnings("ConstantConditions")
238-
private void logExecutorWarning(MethodParameter returnType) {
239-
if (this.taskExecutorWarning && logger.isWarnEnabled()) {
240-
synchronized (this) {
241-
if (this.taskExecutorWarning) {
242-
String executorTypeName = this.taskExecutor.getClass().getSimpleName();
243-
logger.warn("\n!!!\n" +
244-
"Streaming through a reactive type requires an Executor to write to the response.\n" +
245-
"Please, configure a TaskExecutor in the MVC config under \"async support\".\n" +
246-
"The " + executorTypeName + " currently in use is not suitable under load.\n" +
247-
"-------------------------------\n" +
248-
"Controller:\t" + returnType.getContainingClass().getName() + "\n" +
249-
"Method:\t\t" + returnType.getMethod().getName() + "\n" +
250-
"Returning:\t" + ResolvableType.forMethodParameter(returnType) + "\n" +
251-
"!!!");
252-
this.taskExecutorWarning = false;
253-
}
254-
}
255-
}
256-
}
257-
258228

259229
private abstract static class AbstractEmitterSubscriber implements Subscriber<Object>, Runnable {
260230

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
169169
@Nullable
170170
private MethodValidator methodValidator;
171171

172-
private AsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("MvcAsync");
172+
private AsyncTaskExecutor taskExecutor = new MvcSimpleAsyncTaskExecutor();
173173

174174
@Nullable
175175
private Long asyncRequestTimeout;
@@ -1041,4 +1041,37 @@ private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
10411041
return mav;
10421042
}
10431043

1044+
1045+
/**
1046+
* A default Spring MVC AsyncTaskExecutor that warns if used.
1047+
*/
1048+
@SuppressWarnings("serial")
1049+
private class MvcSimpleAsyncTaskExecutor extends SimpleAsyncTaskExecutor {
1050+
1051+
private static Boolean taskExecutorWarning = true;
1052+
1053+
public MvcSimpleAsyncTaskExecutor() {
1054+
super("MvcAsync");
1055+
}
1056+
1057+
@Override
1058+
public void execute(Runnable task) {
1059+
if (taskExecutorWarning && logger.isWarnEnabled()) {
1060+
synchronized (this) {
1061+
if (taskExecutorWarning) {
1062+
logger.warn("""
1063+
!!!
1064+
Performing asynchronous handling through the default Spring MVC SimpleAsyncTaskExecutor.
1065+
This executor is not suitable for production use under load.
1066+
Please, configure an AsyncTaskExecutor through the WebMvc config.
1067+
-------------------------------
1068+
!!!""");
1069+
taskExecutorWarning = false;
1070+
}
1071+
}
1072+
}
1073+
super.execute(task);
1074+
}
1075+
}
1076+
10441077
}

0 commit comments

Comments
 (0)