I have Spring MVC Service with SSE API having controller return type ResponseEntity<SseEmitter>.
and in background Async thread, we send continuous data messages to the client over SseEmitter
@controller method
@PostMapping(value = "/sse", consumes = { "application/json"},produces = {"text/event-stream"})
public ResponseEntity<SseEmitter> executeSseStreaming(
HttpServletRequest request, final @Valid @RequestBody ApiRequestDto apiRequestDto) throws JsonProcessingException {
String commandWithArgs = apiRequestDto.getCommand();
return new ResponseEntity<>(Service.executeServiceStreaming(commandWithArgs),
HttpStatus.OK);
}
@service method
@Override
public SseEmitter executeServiceStreaming(String command) {
SseEmitter sseEmitter = new SseEmitter(15 * 60 * 1000L);
commandInvoker.invokeAsyncMethodStreaming(command, sseEmitter);// background async task
return sseContext.getSseEmitter();
}
@component method
@Async("cmdAsyncCall")
public void invokeAsyncMethodStreaming(String cliBuff, SseEmitter sseEmitter) {
sock = connectionManagerFactory.getCommandExecConnection(cliBuff);
try (BufferedReader reader = new BufferedReader(new InputStreamReader(sock.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
sseEmitter().send(line);
}
sseContext.getSseEmitter().complete();
} catch (Exception e) {
sseContext.getSseEmitter().completeWithError(e);
throw new CustomException(ErrorConstants.EC_SOCKET_READ_FAILED, e);
}
}
Async config bean
@Configuration
@EnableAsync(proxyTargetClass = true)
public class CustomeAsyncCallConfig {
@Bean(name = "cmdAsyncCall")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("SSE Thread: AsyncCall -");
executor.initialize();
return executor;
}
}
after all data has been sent, Async thread calls SseEmmiter.complete() and that trigers below error in catalina.log
12-Dec-2024 14:16:03.364 SEVERE [catalina-exec-7] org.apache.catalina.connector.CoyoteAdapter.asyncDispatch Exception while processing an asynchronous request java.lang.IllegalStateException: Cannot call sendError() after the response has been committed at org.apache.catalina.connector.Response.sendError(Response.java:1110) at org.apache.catalina.connector.Response.sendError(Response.java:1092) at org.apache.catalina.valves.RequestFilterValve.denyRequest(RequestFilterValve.java:394) at org.apache.catalina.valves.RequestFilterValve.process(RequestFilterValve.java:364) at org.apache.catalina.valves.RemoteAddrValve.invoke(RemoteAddrValve.java:54) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:239) at org.apache.coyote.AbstractProcessor.dispatch(AbstractProcessor.java:243) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:57) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:905) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1741) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190) at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) at java.base/java.lang.Thread.run(Unknown Source)
tried below things for this issue
-
Returning SseEmmiter object directly from Controller API method,
-
Using custom Executor service for async processing work and removing @async.
but that also causing same issue. Client able to get all data , connection also gets closed at client side after receiving all data in postman . but on server this Error happens at very end in both.
when debuged I could see error happening just after emitter.complete() method call in Async thread.
tried executing API also from custom java client, but same behaviour observed. I am able to get all data at client side with connection close at the end. but server side have this specific exception in catalina logs. thread name in stacktrace denotes its different than Async threadname, and might be triggered by spring itself.
I am new to Server Sent Events . any help will be greatly appreciated. Thanks in advance.