How to create multiple asynchronous java filters?

asynchronous, filter, java, servlet-filters, servlets

Solution

There are two parts to this answer.

1) The `chain.doFilter(request, response);` is still required.

2) The reason this was not working is that in each filter and in the servlet I was calling `request.startAsync()`, which started a new async process, rather than using an existing one. So if the filter started an async process, and the servlet also started one, it would overwrite/ignore the one started in the filter. To solve this you must check to see if an async process is already started, by calling `request.isAsyncStarted()`, and if it is, rather than starting a new async context, you should get the existing one with `request.getAsyncContext()`. Below is a helper class I created to do this for each servlet and filter, so that I can just call `AsyncHelper.getAsyncContext(request, response)` and it will either retrieve the existing AsyncContext, or create a new one.

public class AsyncHelper {
    public static AsyncContext getAsyncContext(ServletRequest request, ServletResponse response) {
        AsyncContext asyncContext = null;
        if (request.isAsyncStarted()) {
            asyncContext = request.getAsyncContext();
        }
        else {
            asyncContext = request.startAsync(request, response);
            asyncContext.setTimeout(2000);
        }
        return asyncContext;
    }
}

Problem

I'm trying to create a Java application with multiple asynchronous filters, but cannot seem to get them to work well together. I think the main issue is in the run() method I don't know what to do to pass along the request to the next filter in the chain. I've tried `chain.doFilter(request, response)`, but that doesn't seem to work, and there are `dispatch()` and `complete()` APIs available on the `AsyncContext`, but those seem to close out the entire AsyncContext. It seems like there must be another way to get this to work. Below is a snippet of the filter I'm using - the second filter looks almost identical. Note: I'm adding headers to try and figure out what is getting called. ``` @Override public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException { final AsyncContext asyncContext = request.startAsync(); final HttpServletResponse res = (HttpServletResponse) response; asyncContext.addListener(new AsyncListener() { @Override public void onComplete(AsyncEvent event) throws IOException { res.addHeader("S-AST2", "onComplete"); } @Override public void onTimeout(AsyncEvent event) throws IOException { res.addHeader("S-AST3", "onTimeout"); } @Override public void onError(AsyncEvent event) throws IOException { res.addHeader("S-AST4", "onError"); } @Override public void onStartAsync(AsyncEvent event) throws IOException { res.addHeader("S-AST0", "onStartAsync"); } }); asyncContext.start(new Runnable() { @Override public void run() { res.addHeader("S-AST1", "before"); // This doesn't seem to work... asyncContext.dispatch(); // ... or this ... asyncContext.complete(); // ... or this ... chain.doFilter(request, response); } }); } ``` Thanks for any insight!

Original source