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!
We can use the submit method of the ExecutorService to perform the task asynchronously and return the instance of the FutureTask. So let's find the factorial of a number: ExecutorService threadpool = Executors. newCachedThreadPool(); Future<Long> futureTask = threadpool.
The doFilter method of the Filter is called by the container each time a request/response pair is passed through the chain due to a client request for a resource at the end of the chain. The FilterChain passed in to this method allows the Filter to pass on the request and response to the next entity in the chain.
There isn't anything native to java that lets you do this like async/await keywords, but what you can do if you really want to is use a CountDownLatch.
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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With