Get HttpServletRequest state in order to avoid IllegalStateException

3k Views Asked by At

Sometimes the method req.startAsync() from HttpServletRequest throws an IllegalStateException (Response has been closed already).

How can I check the request state in order to avoid IllegalStateException?

req.isAsyncSupported() returns true always.

Relevant code:

final HttpSession httpSession = req.getSession(false);  
if (httpSession == null) {  
    resp.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);  
    return;  
}  
final AsyncContext asynContext = req.startAsync(); //exception here`

Exception:

12:28:12,735 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/esus].[LongPollServlet]] (http-/0.0.0.0:8080-2) JBWEB000236: Servlet.service() for servlet LongPollServlet threw exception: java.lang.IllegalStateException: JBWEB000049: Response has been closed already at org.apache.catalina.connector.Request.startAsync(Request.java:3180) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1] at org.apache.catalina.connector.Request.startAsync(Request.java:3170) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1] at org.apache.catalina.connector.RequestFacade.startAsync(RequestFacade.java:925) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1] at com.performer.framework.server.LongPollServlet.continueDoPost(LongPollServlet.java:75) [st10.framework.webserver.jar:] at com.performer.framework.server.LongPollServlet.doPost(LongPollServlet.java:62) [st10.framework.webserver.jar:] at javax.servlet.http.HttpServlet.service(HttpServlet.java:754) [jboss-servlet-api_3.0_spec-1.0.2.Final-redhat-1.jar:1.0.2.Final-redhat-1] at javax.servlet.http.HttpServlet.service(HttpServlet.java:847) [jboss-servlet-api_3.0_spec-1.0.2.Final-redhat-1.jar:1.0.2.Final-redhat-1] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:295) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:149) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1] at org.jboss.as.jpa.interceptor.WebNonTxEmCloserValve.invoke(WebNonTxEmCloserValve.java:50) [jboss-as-jpa-7.2.0.Final-redhat-8.jar:7.2.0.Final-redhat-8] at org.jboss.as.jpa.interceptor.WebNonTxEmCloserValve.invoke(WebNonTxEmCloserValve.java:50) [jboss-as-jpa-7.2.0.Final-redhat-8.jar:7.2.0.Final-redhat-8] at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:169) [jboss-as-web-7.2.0.Final-redhat-8.jar:7.2.0.Final-redhat-8] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:145) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:97) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:102) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:336) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1] at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:856) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1] at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:653) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1] at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:915) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1] at java.lang.Thread.run(Unknown Source) [rt.jar:1.6.0_34]

2

There are 2 best solutions below

6
On

Acording to documentation for startAsync():

Throws: IllegalStateException - if this request is within the scope of a filter or servlet that does not support asynchronous operations (that is, isAsyncSupported() returns false), or if this method is called again without any asynchronous dispatch (resulting from one of the AsyncContext#dispatch methods), is called outside the scope of any such dispatch, or is called again within the scope of the same dispatch, or if the response has already been closed.

It seems that Tomcat has isAsyncSupported() set to true.

You can set the async attribute in the HttpServletRequest object to true by:

req.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);

or

((org.apache.catalina.connector.Request)req).setAsyncSupported(true);

You also have to add in front of your servlet:

@WebServlet(... , asyncSupported = true)
2
On

You want to check the response, not the request state, as startAsync() cannot be called when the original response is closed.

You should be able to see from your code where is closes the response (e.g. if it calls response.close(); somewhere). You should call startAsync() before this point, or set a flag once it has been called and check that before calling startAsync().

If you do not handle the response at all you can try to test The HttpServletResponse to see if it's been closed, but there is no definite way to do that:

if (!isClosed(response.getOutputStream())) {
  startAsync();
  // ...

For discussion of implementation of isClosed(), see this answer. There is no easy or recommended way, but if you're desparate this may work (untested) but it does write to the response output stream, which is dangerous:

boolean isClosed(ServletOutputStream sos) {
  try {
    os.println("");  // This will append extra line feed to the HTTP response!
    return false;
  } catch(IOException e) {
    return true;  // assume due to stream being closed
  }
}

Alternatively, you can possibly rely on response.isCommitted() (see doc) as a committed response would typically be closed, however this isn't definite - it could be closed but not committed.