Consolidate default WebMvc executor log warnings
Closes gh-30902
This commit is contained in:
		
							parent
							
								
									8a283e39ff
								
							
						
					
					
						commit
						4becce1c2b
					
				| 
						 | 
				
			
			@ -30,7 +30,6 @@ 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;
 | 
			
		||||
| 
						 | 
				
			
			@ -74,8 +73,6 @@ public final class WebAsyncManager {
 | 
			
		|||
	private static final DeferredResultProcessingInterceptor timeoutDeferredResultInterceptor =
 | 
			
		||||
			new TimeoutDeferredResultProcessingInterceptor();
 | 
			
		||||
 | 
			
		||||
	private static Boolean taskExecutorWarning = true;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	@Nullable
 | 
			
		||||
	private AsyncWebRequest asyncWebRequest;
 | 
			
		||||
| 
						 | 
				
			
			@ -295,9 +292,6 @@ public final class WebAsyncManager {
 | 
			
		|||
		if (executor != null) {
 | 
			
		||||
			this.taskExecutor = executor;
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			logExecutorWarning();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		List<CallableProcessingInterceptor> interceptors = new ArrayList<>();
 | 
			
		||||
		interceptors.add(webAsyncTask.getInterceptor());
 | 
			
		||||
| 
						 | 
				
			
			@ -357,26 +351,6 @@ public final class WebAsyncManager {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,7 +38,6 @@ import org.springframework.core.MethodParameter;
 | 
			
		|||
import org.springframework.core.ReactiveAdapter;
 | 
			
		||||
import org.springframework.core.ReactiveAdapterRegistry;
 | 
			
		||||
import org.springframework.core.ResolvableType;
 | 
			
		||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
 | 
			
		||||
import org.springframework.core.task.SyncTaskExecutor;
 | 
			
		||||
import org.springframework.core.task.TaskExecutor;
 | 
			
		||||
import org.springframework.http.MediaType;
 | 
			
		||||
| 
						 | 
				
			
			@ -91,8 +90,6 @@ class ReactiveTypeHandler {
 | 
			
		|||
 | 
			
		||||
	private final ContentNegotiationManager contentNegotiationManager;
 | 
			
		||||
 | 
			
		||||
	private boolean taskExecutorWarning;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	public ReactiveTypeHandler() {
 | 
			
		||||
		this(ReactiveAdapterRegistry.getSharedInstance(), new SyncTaskExecutor(), new ContentNegotiationManager());
 | 
			
		||||
| 
						 | 
				
			
			@ -105,9 +102,6 @@ class ReactiveTypeHandler {
 | 
			
		|||
		this.adapterRegistry = registry;
 | 
			
		||||
		this.taskExecutor = executor;
 | 
			
		||||
		this.contentNegotiationManager = manager;
 | 
			
		||||
 | 
			
		||||
		this.taskExecutorWarning =
 | 
			
		||||
				(executor instanceof SimpleAsyncTaskExecutor || executor instanceof SyncTaskExecutor);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -147,20 +141,17 @@ class ReactiveTypeHandler {
 | 
			
		|||
		if (adapter.isMultiValue()) {
 | 
			
		||||
			if (mediaTypes.stream().anyMatch(MediaType.TEXT_EVENT_STREAM::includes) ||
 | 
			
		||||
					ServerSentEvent.class.isAssignableFrom(elementClass)) {
 | 
			
		||||
				logExecutorWarning(returnType);
 | 
			
		||||
				SseEmitter emitter = new SseEmitter(STREAMING_TIMEOUT_VALUE);
 | 
			
		||||
				new SseEmitterSubscriber(emitter, this.taskExecutor).connect(adapter, returnValue);
 | 
			
		||||
				return emitter;
 | 
			
		||||
			}
 | 
			
		||||
			if (CharSequence.class.isAssignableFrom(elementClass)) {
 | 
			
		||||
				logExecutorWarning(returnType);
 | 
			
		||||
				ResponseBodyEmitter emitter = getEmitter(mediaType.orElse(MediaType.TEXT_PLAIN));
 | 
			
		||||
				new TextEmitterSubscriber(emitter, this.taskExecutor).connect(adapter, returnValue);
 | 
			
		||||
				return emitter;
 | 
			
		||||
			}
 | 
			
		||||
			MediaType streamingResponseType = findConcreteStreamingMediaType(mediaTypes);
 | 
			
		||||
			if (streamingResponseType != null) {
 | 
			
		||||
				logExecutorWarning(returnType);
 | 
			
		||||
				ResponseBodyEmitter emitter = getEmitter(streamingResponseType);
 | 
			
		||||
				new JsonEmitterSubscriber(emitter, this.taskExecutor).connect(adapter, returnValue);
 | 
			
		||||
				return emitter;
 | 
			
		||||
| 
						 | 
				
			
			@ -234,27 +225,6 @@ class ReactiveTypeHandler {
 | 
			
		|||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@SuppressWarnings("ConstantConditions")
 | 
			
		||||
	private void logExecutorWarning(MethodParameter returnType) {
 | 
			
		||||
		if (this.taskExecutorWarning && logger.isWarnEnabled()) {
 | 
			
		||||
			synchronized (this) {
 | 
			
		||||
				if (this.taskExecutorWarning) {
 | 
			
		||||
					String executorTypeName = this.taskExecutor.getClass().getSimpleName();
 | 
			
		||||
					logger.warn("\n!!!\n" +
 | 
			
		||||
							"Streaming through a reactive type requires an Executor to write to the response.\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" +
 | 
			
		||||
							"Controller:\t" + returnType.getContainingClass().getName() + "\n" +
 | 
			
		||||
							"Method:\t\t" + returnType.getMethod().getName() + "\n" +
 | 
			
		||||
							"Returning:\t" + ResolvableType.forMethodParameter(returnType) + "\n" +
 | 
			
		||||
							"!!!");
 | 
			
		||||
					this.taskExecutorWarning = false;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	private abstract static class AbstractEmitterSubscriber implements Subscriber<Object>, Runnable {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -169,7 +169,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
 | 
			
		|||
	@Nullable
 | 
			
		||||
	private MethodValidator methodValidator;
 | 
			
		||||
 | 
			
		||||
	private AsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("MvcAsync");
 | 
			
		||||
	private AsyncTaskExecutor taskExecutor = new MvcSimpleAsyncTaskExecutor();
 | 
			
		||||
 | 
			
		||||
	@Nullable
 | 
			
		||||
	private Long asyncRequestTimeout;
 | 
			
		||||
| 
						 | 
				
			
			@ -1041,4 +1041,37 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
 | 
			
		|||
		return mav;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * A default Spring MVC AsyncTaskExecutor that warns if used.
 | 
			
		||||
	 */
 | 
			
		||||
	@SuppressWarnings("serial")
 | 
			
		||||
	private class MvcSimpleAsyncTaskExecutor extends SimpleAsyncTaskExecutor {
 | 
			
		||||
 | 
			
		||||
		private static Boolean taskExecutorWarning = true;
 | 
			
		||||
 | 
			
		||||
		public MvcSimpleAsyncTaskExecutor() {
 | 
			
		||||
			super("MvcAsync");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public void execute(Runnable task) {
 | 
			
		||||
			if (taskExecutorWarning && logger.isWarnEnabled()) {
 | 
			
		||||
				synchronized (this) {
 | 
			
		||||
					if (taskExecutorWarning) {
 | 
			
		||||
						logger.warn("""
 | 
			
		||||
								!!!
 | 
			
		||||
								Performing asynchronous handling through the default Spring MVC SimpleAsyncTaskExecutor.
 | 
			
		||||
								This executor is not suitable for production use under load.
 | 
			
		||||
								Please, configure an AsyncTaskExecutor through the WebMvc config.
 | 
			
		||||
								-------------------------------
 | 
			
		||||
								!!!""");
 | 
			
		||||
						taskExecutorWarning = false;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			super.execute(task);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue