Trouble with Spring async request processing in Controller - getting java.lang.IllegalStateException: Cannot forward after response has been committed

user2166452

Any help in figuring this out is much appreciated!

Is should mention - I'm not using jsp. I'm using js and jquery and this is a REST API call, but I'm not using any formal Spring REST support at this point. Just writing responses directly. So perhaps there's something specific to that.

I'm trying to do a "simple test" of the feature before integrating it into my code base. I've read the MVC documentation and am following this example: https://github.com/spring-projects/spring-mvc-showcase/blob/master/src/main/java/org/springframework/samples/mvc/async/DeferredResultController.java

I have true in my web.xml file in every Filter and my one Servlet definition.

My servlet context file has this:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean
            class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper" ref="jacksonObjectMapper" />
        </bean>
    </mvc:message-converters>
    <mvc:async-support default-timeout="3000" />
</mvc:annotation-driven>

And I've got my task executor setup as such:

<task:annotation-driven executor="taskExecutor"
        scheduler="taskScheduler" />
    <task:executor id="taskExecutor" pool-size="1-25"
        queue-capacity="100" rejection-policy="CALLER_RUNS" />

    <task:scheduler id="taskScheduler" pool-size="10" />

According to docs, the Spring default is to use the ThreadPoolTaskExecutor which implements AsyncTaskExecutor so I think I should be all set there: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.html

I have a very simple controller method and scheduled task to return and set status on a DeferredResult:

@RequestMapping(value = "/collectMetaData", method = RequestMethod.POST)
public DeferredResult<ResponseEntity<String>> collectMetaDataDeferredResult(
        @ModelAttribute @Valid CollectMetaDataForm collectMetaDataForm,
        BindingResult result, HttpSession session) {
    DeferredResult<ResponseEntity<String>> response = new DeferredResult<ResponseEntity<String>>();
    this.responseBodyQueue.add(response);
    return response;
}

@Scheduled(fixedRate = 2000)
public void processQueues() {
    for (DeferredResult<ResponseEntity<String>> result : this.responseBodyQueue) {
        result.setResult(new ResponseEntity<String>("cancelled",
                HttpStatus.OK));
        this.responseBodyQueue.remove(result);
    }
}

The app boots up and seems to work fine, but when I call this controller method I get this error:

java.lang.IllegalStateException: Cannot forward after response has been committed
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:349)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:339)
    at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:467)
    at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:338)
    at org.apache.catalina.core.StandardHostValve.throwable(StandardHostValve.java:428)
    at org.apache.catalina.core.AsyncContextImpl.setErrorState(AsyncContextImpl.java:436)
    at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:295)
    at org.apache.coyote.http11.AbstractHttp11Processor.asyncDispatch(AbstractHttp11Processor.java:1636)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:599)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:315)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)

At this point I'm not even sure if async request processing is working at all. It appears to be because of the asyncDispatch call.

user2166452

Short answer: code was good, I just needed to move the TaskExecutor specification into the same config file with the spring-servlet definition (and async-support stuff).

The error seems to be the default when a DeferredResult experiences its timeout.

My first troubleshooting was to increase the timeout - which increased the amount of time I waited before getting the error. This seemed to indicate that the request was entering async mode correctly and the DeferredResult was being returned (and not generating an error), but the setResult wasn't being called before the timeout setting.

So, I put some print statements into that @Scheduled method and sure enough, nothing in there was happening. There's two sections of XML in the original question above. The first was in my spring.xml config file, and the second was in a separate applicationConfig.xml file. I put them both in the spring file and it worked.

I believe ultimately the error was based on the scope of the component scan inside of my spring.xml file. This has burned me again and again. This gets esoteric but fairly simple - we use spring-security and PasswordEncoder to salt and hash passwords. For scope reasons that are above my head, the PasswordEncoder config can't be specified in a file with a component scan that hits the DAO that it is used in (counterintuitively). So, we have 3 XML config files in total, 2 with component scans and exclusion filters to make sure they don't overlap. Occasionally I put some config in the wrong file and it can't be accessed by the classes in the component scan that want it. If I could figure out how to have only one component scan it would solve all of this, but once in a while something like this comes up.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

java.lang.IllegalStateException: Cannot forward after response has been committed after add `return`

java.lang.IllegalStateException: Cannot forward after response has been committed in servlet

Spring java.lang.IllegalStateException: Cannot create a session after the response has been committed

Spring boot : java.lang.IllegalStateException: Cannot call sendError() after the response has been committed

java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed at or.apache.catalina.connector.ResponseFacade

Cannot forward after response has been committed in Servlet

Cannot forward after response has been committed Servlet error

Cannot forward after response has been committed in servlet filter

Spring/Thymeleaf throws "Cannot create a session after the response has been committed" when processing @PostMapping

spring @SessionAttributes and "Cannot create a session after the response has been committed"

Rewrite / PrettyFaces with parameter causes IllegalStateException: Cannot create a session after the response has been committed

java.lang.IllegalStateException: Cannot call getInputStream() after getReader() has already been called for the current request

Cannot forward to error page for request ... as the response has already been committed. As a result, the response may have the wrong status code

how to fix Cannot call sendRedirect() after the response has been committed?

Servlet error: Cannot call sendRedirect() after the response has been committed

I am getting an exception: java.lang.IllegalStateException: getOutputStream() has already been called for this response

Is it possible to forward or redirect from a servlet filter after the response has been committed?

java.lang.IllegalStateException: getReader() has already been called for this request

Cannot call sendRedirect() after the response has been committed (after sending post form)

Cannot call sendRedirect() after the response has been committed in Struts2

Upload File To Server, java.net.ProtocolException: cannot write request body after response has been read

REST - java.lang.IllegalStateException: getOutputStream() has already been called for this response

Servlet - java.lang.IllegalStateException: getWriter() has already been called for this response

Getting the error "Java.lang.IllegalStateException Activity has been destroyed" when using tabs with ViewPager

java.lang.IllegalStateException: Request Data has already been read CQ5,AEM

Getting NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException: Ambiguous handler methods mapped

Server Send Requests firebase java.net.protocolexception cannot write request body after response has been read

java.lang.IllegalStateException: Can't get ClosableLazy value after it has been closed

Spring batch - java.lang.IllegalStateException: InputStream has already been read - do not use InputStreamResource