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.
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.
Comments