Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [jetty-dev] Calling startAsync() on a servlet request throws java.lang.IllegalStateException: s=DISPATCHED i=true a=STARTED

Hi,

On Fri, Feb 24, 2017 at 12:37 PM, Sanjay Bhat
<Sanjay.Bhat@xxxxxxxxxxxxxx> wrote:
> Hi Simone,
>
>
>
>> The real question is: why you call startAsync() in your filter and then
>> chain to the ProxyServlet?
>
>
>
> Using AsyncListener, I want to wait for the "completion" of the requests
> whose URLs are mapped to my filter, so that I can perform some specific
> operations in the AsyncListener's onComplete() method. For example, if the
> request to create a search object in Kibana is completed, I want to create a
> new object in my application and also generate an audit event.
>
>
>
> Anyway, I fixed the IllegalStateException as follows:
>
> 1.      The listener related code segment was located BEFORE the filter’s
> “chain.doFilter()” call. I moved this code segment to a position AFTER the
> filter’s "chain.doFilter()" call, so that I can retrieve the AsyncContext
> that is "already created" in the Jetty ProxyServlet's service() method. I
> can then add an AsyncListener to the retrieved AsyncContext.
>
> 2.      I changed the “request.startAsync()” call in my filter to
> “request.getAsyncContext()” so that I am not starting a new AsyncContext
> which leads to the IllegalStateException, but only retrieving the
> AsyncContext that is already created in Jetty’s ProxyServlet.
>
>
>
> So the updated code segment looks like this:

[snip]

I don't think your code is right because it assumes too much about
down-the-chain filters/servlets.
Furthermore, your code has a race: if the servlet completes the
AsyncContext _before_ you can register the listener, it will never be
called.

The right way should be:

Object async = request.getAttribute("async");
if (async == null) {
  AsyncContext ctx = request.startAsync();
  ctx.setTimeout(0);
  request.setAttribute("async", true);
  ctx.addListener(...);
  ctx.dispatch();
} else {
  chain.doFilter(request, response);
}

In this way you don't depend on filter order or on the fact that other
components may or not call startAsync().

With the solution above, you start an async cycle, register the
listener, then dispatch.
The dispatch will cause the request to re-enter the filter chain, but
this time on the else branch where you forward it along the chain.
No races, everything self contained in your filter, no dependencies on
what other components do.

-- 
Simone Bordet
----
http://cometd.org
http://webtide.com
Developer advice, training, services and support
from the Jetty & CometD experts.


Back to the top