4

I am using Spring MVC 4.3.9.RELEASE, Java 8, Tomcat 7

My code is as below,

@Controller
@EnableWebMvc
public class StreamRecordsController {

    @RequestMapping(value = "/streamrecords/{elementname}", method = RequestMethod.GET, 
                    produces = "application/json; charset=UTF-8")
    @ResponseBody
    public ResponseEntity<StreamingResponseBody> streamRecords(
            HttpServletRequest request, 
            HttpServletResponse response,
            @PathVariable(value = "elementname", required = true) String elementName,
            @RequestParam(value = "customerid", required = true) long customerId,
            @RequestParam(value = "userid", required = true) long userId) throws Exception {
            StreamingResponseBody responseBody = outputStream -> {
                    /**
                    * 1. FETCH Data from MongoDB using dbcursor and convert to json using pagination.
                    * 2. Write json to outputStream.
                    */
                };
            return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=sample.json")
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(responseBody);
        }

}

I am getting error as below,

May 10, 2019 11:45:41 AM org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver handleAsyncRequestTimeoutException
SEVERE: Async timeout for GET [/server/streamrecords/xyz]
May 10, 2019 11:46:01 AM org.apache.catalina.connector.CoyoteAdapter checkRecycled
INFO: Encountered a non-recycled response and recycled it forcedly.
org.apache.catalina.connector.CoyoteAdapter$RecycleRequiredException
        at org.apache.catalina.connector.CoyoteAdapter.checkRecycled(CoyoteAdapter.java:634)
        at org.apache.coyote.http11.AbstractHttp11Processor.recycle(AbstractHttp11Processor.java:1909)
        at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.release(Http11AprProtocol.java:245)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:720)
        at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2574)
        at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2563)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)

org.apache.catalina.connector.ClientAbortException: java.io.IOException
        at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:370)
        at org.apache.catalina.connector.OutputBuffer.flush(OutputBuffer.java:334)
        at org.apache.catalina.connector.CoyoteOutputStream.flush(CoyoteOutputStream.java:101)
        at com.entrib.emg.server.rest.api.services.ElementService.lambda$streamRecords$1(ElementService.java:170)
        at org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler$StreamingResponseBodyTask.call(StreamingResponseBodyReturnValueHandler.java:106)
        at org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler$StreamingResponseBodyTask.call(StreamingResponseBodyReturnValueHandler.java:93)
        at org.springframework.web.context.request.async.WebAsyncManager$4.run(WebAsyncManager.java:316)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.lang.Thread.run(Thread.java:748)
Caused by: java.io.IOException
        at org.apache.coyote.http11.InternalAprOutputBuffer.flushBuffer(InternalAprOutputBuffer.java:205)
        at org.apache.coyote.http11.InternalAprOutputBuffer.flush(InternalAprOutputBuffer.java:109)
        at org.apache.coyote.http11.AbstractHttp11Processor.action(AbstractHttp11Processor.java:850)
        at org.apache.coyote.Response.action(Response.java:171)
        at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:366)
        ... 9 more

QUESTION:

What am I missing? How can I make my async request not timeout and wait until the outputStream is gracefully shutdown?

2 Answers 2

4

The Only thing I had to do was to extend org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; and override its method configureAsyncSupport(AsyncSupportConfigurer configurer). You can set the timeout using below code.

@Controller
@EnableWebMvc
public class StreamRecordsController extends WebMvcConfigurerAdapter{

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        configurer.setDefaultTimeout(100000000); //in milliseconds (20 hours)
        super.configureAsyncSupport(configurer);
    }

    @RequestMapping(value = "/streamrecords/{elementname}", method = RequestMethod.GET, 
                    produces = "application/json; charset=UTF-8")
    @ResponseBody
    public ResponseEntity<StreamingResponseBody> streamRecords(
            HttpServletRequest request, 
            HttpServletResponse response,
            @PathVariable(value = "elementname", required = true) String elementName,
            @RequestParam(value = "customerid", required = true) long customerId,
            @RequestParam(value = "userid", required = true) long userId) throws Exception {
            StreamingResponseBody responseBody = outputStream -> {
                    /**
                    * 1. FETCH Data from MongoDB using dbcursor and convert to json using pagination.
                    * 2. Write json to outputStream.
                    */
                };
            return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=sample.json")
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(responseBody);
        }

}
Sign up to request clarification or add additional context in comments.

1 Comment

Is it not safe to set such high value for request timeout? I imagine it may take out one thread for 20 hours. I was hoping that there is solution to not throw timeout exception as long as stream is being processed
3

Since WebMvcConfigurerAdapter is now deprecated, the class now has to implement WebMvcConfigurer and Ankur's answer was successfully rewritten as :

@RestController
@RequestMapping("/api")
@Log4j2
public class StreamRecordsController implements WebMvcConfigurer { 

    @Override  
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        configurer.setDefaultTimeout(2_000_000); 
    }   

    ...

Tested with Spring Boot 2.4.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.