Warn when SimpleAsyncTaskExecutor is used

Issue: SPR-16203
This commit is contained in:
Rossen Stoyanchev
2018-07-11 11:07:25 -04:00
parent 1b1bc7f5b5
commit 7b3a72f483
6 changed files with 96 additions and 37 deletions

View File

@@ -30,10 +30,12 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.async.DeferredResult.DeferredResultHandler;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
/**
* The central class for managing asynchronous request processing, mainly intended
@@ -61,6 +63,9 @@ public final class WebAsyncManager {
private static final Object RESULT_NONE = new Object();
private static final AsyncTaskExecutor DEFAULT_TASK_EXECUTOR =
new SimpleAsyncTaskExecutor(WebHttpHandlerBuilder.class.getSimpleName());
private static final Log logger = LogFactory.getLog(WebAsyncManager.class);
private static final CallableProcessingInterceptor timeoutCallableInterceptor =
@@ -69,10 +74,12 @@ public final class WebAsyncManager {
private static final DeferredResultProcessingInterceptor timeoutDeferredResultInterceptor =
new TimeoutDeferredResultProcessingInterceptor();
private static Boolean taskExecutorWarning = true;
private AsyncWebRequest asyncWebRequest;
private AsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor(this.getClass().getSimpleName());
private AsyncTaskExecutor taskExecutor = DEFAULT_TASK_EXECUTOR;
private volatile Object concurrentResult = RESULT_NONE;
@@ -277,6 +284,9 @@ public final class WebAsyncManager {
if (executor != null) {
this.taskExecutor = executor;
}
else {
logExecutorWarning();
}
List<CallableProcessingInterceptor> interceptors = new ArrayList<>();
interceptors.add(webAsyncTask.getInterceptor());
@@ -330,6 +340,27 @@ public final class WebAsyncManager {
}
}
@SuppressWarnings("ConstantConditions")
private void logExecutorWarning() {
if (taskExecutorWarning && logger.isWarnEnabled()) {
synchronized (DEFAULT_TASK_EXECUTOR) {
AsyncTaskExecutor executor = this.taskExecutor;
if (taskExecutorWarning &&
(executor instanceof SimpleAsyncTaskExecutor || executor instanceof SyncTaskExecutor)) {
String executorTypeName = executor.getClass().getSimpleName();
logger.warn("\n!!!\n" +
"An Executor is required to handle java.util.concurrent.Callable return values.\n" +
"Please, configure a TaskExecutor in the MVC config under \"async support\".\n" +
"The " + executorTypeName + " currently in use is not suitable under load.\n" +
"-------------------------------\n" +
"Request URI: '" + formatRequestUri() + "'\n" +
"!!!");
taskExecutorWarning = false;
}
}
}
}
private String formatRequestUri() {
HttpServletRequest request = this.asyncWebRequest.getNativeRequest(HttpServletRequest.class);
return request != null ? request.getRequestURI() : "servlet container";

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -115,7 +115,7 @@ public class WebAsyncManagerTests {
verifyDefaultAsyncScenario();
verify(interceptor).beforeConcurrentHandling(this.asyncWebRequest, task);
verify(interceptor).preProcess(this.asyncWebRequest, task);
verify(interceptor).postProcess(this.asyncWebRequest, task, new Integer(concurrentResult));
verify(interceptor).postProcess(this.asyncWebRequest, task, concurrentResult);
}
@Test
@@ -161,9 +161,9 @@ public class WebAsyncManagerTests {
assertFalse(this.asyncManager.hasConcurrentResult());
verify(this.asyncWebRequest).addTimeoutHandler((Runnable) notNull());
verify(this.asyncWebRequest).addErrorHandler((Consumer<Throwable>) notNull());
verify(this.asyncWebRequest).addCompletionHandler((Runnable) notNull());
verify(this.asyncWebRequest).addTimeoutHandler(notNull());
verify(this.asyncWebRequest).addErrorHandler(notNull());
verify(this.asyncWebRequest).addCompletionHandler(notNull());
}
@Test
@@ -303,9 +303,9 @@ public class WebAsyncManagerTests {
assertFalse(this.asyncManager.hasConcurrentResult());
verify(this.asyncWebRequest).addTimeoutHandler((Runnable) notNull());
verify(this.asyncWebRequest).addErrorHandler((Consumer<Throwable>) notNull());
verify(this.asyncWebRequest).addCompletionHandler((Runnable) notNull());
verify(this.asyncWebRequest).addTimeoutHandler(notNull());
verify(this.asyncWebRequest).addErrorHandler(notNull());
verify(this.asyncWebRequest).addCompletionHandler(notNull());
}
@Test
@@ -353,7 +353,7 @@ public class WebAsyncManagerTests {
@Test
public void startDeferredResultProcessingNullInput() throws Exception {
try {
this.asyncManager.startDeferredResultProcessing((DeferredResult<?>) null);
this.asyncManager.startDeferredResultProcessing(null);
fail("Expected exception");
}
catch (IllegalArgumentException ex) {
@@ -368,9 +368,9 @@ public class WebAsyncManagerTests {
@SuppressWarnings("unchecked")
private void verifyDefaultAsyncScenario() {
verify(this.asyncWebRequest).addTimeoutHandler((Runnable) notNull());
verify(this.asyncWebRequest).addErrorHandler((Consumer<Throwable>) notNull());
verify(this.asyncWebRequest).addCompletionHandler((Runnable) notNull());
verify(this.asyncWebRequest).addTimeoutHandler(notNull());
verify(this.asyncWebRequest).addErrorHandler(notNull());
verify(this.asyncWebRequest).addCompletionHandler(notNull());
verify(this.asyncWebRequest).startAsync();
verify(this.asyncWebRequest).dispatch();
}