Несколько асинхронный контекст в сервлете не работает
Я пытаюсь отработать несколько цепочек асинхронных запросов в сервлетах, и я сталкиваюсь со странным поведением. Не уверен, имеет ли это отношение к коту.
Итак, вот сценарий. У меня есть простое J2EE maven веб-приложение.
У меня есть два сервлета и фильтр. Я пометил их всех с помощью asyncSupported=true. Когда я нажимаю на ссылку в JSP, первый сервлет действительно принимает запрос и порождает новый рабочий поток, используя AsyncContext. Затем рабочий поток записывает что-то в ответ, фиксирует его (как я узнал, это допустимо для асинхронной обработки в сервлетах) и затем отправляет запрос другому сервлету. До этого момента все работает нормально.
Предполагается, что второй сервлет порождает второй рабочий поток, а затем планировалось сделать диспетчеризацию вызова второго рабочего потока (поскольку я также пытался практиковать вызов без параметров для dispatch()), чтобы вернуться ко второму сервлету, который назвал это. Тем не менее, я получаю сообщение об ошибке ниже при вызове startAsync() на втором сервлете
06-Apr-2018 19:04:48.128 WARNING [RMI TCP Connection(5)-127.0.0.1] org.apache.catalina.startup.ContextConfig.validateSecurityRoles Security role name [authSupervisor] used in an <auth-constraint> without being defined in a <security-role>
06-Apr-2018 19:04:48.261 INFO [RMI TCP Connection(5)-127.0.0.1] com.kingshuk.listeners.MyServletContextListener.contextInitialized The servlet class com.kingshuk.servlets.MyAppDynamicServlet is now being registered
06-Apr-2018 19:05:09.025 WARNING [http-nio-8080-exec-8] org.apache.catalina.connector.Request.startAsync Unable to start async because the following classes in the processing chain do not support async []
java.lang.IllegalStateException: A filter or servlet of the current chain does not support asynchronous operations.
at org.apache.catalina.connector.Request.startAsync(Request.java:1636)
at org.apache.catalina.connector.Request.startAsync(Request.java:1628)
at org.apache.catalina.connector.RequestFacade.startAsync(RequestFacade.java:1043)
at javax.servlet.ServletRequestWrapper.startAsync(ServletRequestWrapper.java:378)
at com.kingshuk.servlets.BiggestAsyncSecondServlet.doGet(BiggestAsyncSecondServlet.java:23)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.kingshuk.filters.AsyncRequestLoggingFilter.doFilter(AsyncRequestLoggingFilter.java:25)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:712)
at org.apache.catalina.core.ApplicationDispatcher.doDispatch(ApplicationDispatcher.java:633)
at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:601)
at org.apache.catalina.core.AsyncContextImpl$AsyncRunnable.run(AsyncContextImpl.java:566)
at org.apache.catalina.core.AsyncContextImpl.doInternalDispatch(AsyncContextImpl.java:352)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:196)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:494)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:137)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:651)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:235)
at org.apache.coyote.AbstractProcessor.dispatch(AbstractProcessor.java:228)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:53)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:754)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1376)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Ниже приведены все связанные файлы
Фильтр
@WebFilter(filterName = "AsyncRequestLoggingFilter",
urlPatterns = {"/asyncServlet", "/biggestAsyncRequestTest", "/biggestAsyncRequestTest2"},
asyncSupported = true,
dispatcherTypes = {DispatcherType.ASYNC, DispatcherType.REQUEST})
public class AsyncRequestLoggingFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
System.out.println("<<AsyncRequestLoggingFilter>> Initializing the Filter");
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws ServletException, IOException {
if (DispatcherType.ASYNC.equals(req.getDispatcherType())) {
System.out.println("<<AsyncRequestLoggingFilter>> This is BEFORE calling the doFilter during the ASYNC dispatching");
} else {
System.out.println("<<AsyncRequestLoggingFilter>> This is BEFORE calling the doFilter");
}
chain.doFilter(req, resp);
if (DispatcherType.ASYNC.equals(req.getDispatcherType())) {
System.out.println("<<AsyncRequestLoggingFilter>> This is AFTER returning from the doFilter call after the ASYNC dispatching");
} else {
System.out.println("<<AsyncRequestLoggingFilter>> This is AFTER returning from the doFilter call");
}
}
public void destroy() {
System.out.println("<<AsyncRequestLoggingFilter>> Destroying the Filter");
}
}
Первый сервлет
@WebServlet(name = "BiggestAsyncFirstServlet",
urlPatterns = "/biggestAsyncRequestTest",
asyncSupported = true)
public class BiggestAsyncFirstServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
AsyncContext asyncContext = request.startAsync();
asyncContext.setTimeout(10000);
asyncContext.addListener(asyncContext.createListener(BiggestAsyncContextListener.class));
//asyncContext.start(new BiggestAsyncFirstWorkerThread());
/*
Step 5.Get the reference to the thread pool that was created in the context listener class
when the app was deployed
*/
ThreadPoolExecutor executor = (ThreadPoolExecutor) request.getServletContext().getAttribute("executor");
/*
Step 6.Actually creating the worker thread
and kick starting the thread by calling the run method of the class implementing the runnable interface.
*/
executor.execute(new BiggestAsyncFirstWorkerThread(asyncContext));
System.out.println("Hi I'm the servlet " + getServletName() + " and my job is done");
}
}
Второй сервлет
@WebServlet(name = "BiggestAsyncSecondServlet",
urlPatterns = "/biggestAsyncRequestTest2",
asyncSupported = true)
public class BiggestAsyncSecondServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
AsyncContext asyncContext = request.startAsync();
//asyncContext.setTimeout(10000);
//asyncContext.createListener(BiggestAsyncContextListener.class);
//asyncContext.start(new BiggestAsyncFirstWorkerThread());
/*
Step 5.Get the reference to the thread pool that was created in the context listener class
when the app was deployed
*/
ThreadPoolExecutor executor = (ThreadPoolExecutor) request.getServletContext().getAttribute("executor");
/*
Step 6.Actually creating the worker thread
and kick starting the thread by calling the run method of the class implementing the runnable interface.
*/
executor.execute(new BiggestAsyncSecondWorkerThread(asyncContext));
System.out.println("Hi I'm the servlet " + getServletName() + " and my job is done");
}
}
Первый рабочий поток
public class BiggestAsyncFirstWorkerThread implements Runnable {
private AsyncContext context;
public BiggestAsyncFirstWorkerThread(AsyncContext context) {
this.context = context;
}
@Override
public void run() {
//The idea is to write something to the response and then dispatch.
try {
AsyncRequestProcessor.waitingTime(6000);
PrintWriter writer = context.getResponse().getWriter();
writer.print("<html>\n" +
"<head>\n" +
" <title>User login</title>\n" +
"\n" +
" <link rel=\"stylesheet\" type=\"text/css\" href=\"/" +
context.getRequest().getServletContext().getServletContextName() + "/style/master_css.css\">\n" +
"\n" +
"\n" +
"</head>");
writer.print("<body>\n" +
"<div id=\"allcontent\">");
context.getRequest().getRequestDispatcher("pages/common/header.jsp").
include(context.getRequest(), context.getResponse());
writer.print(" <div id=\"actual_content\">");
context.getResponse().flushBuffer();
context.dispatch("/biggestAsyncRequestTest2");
} catch (IOException | ServletException e) {
e.printStackTrace();
}
}
}
Второй рабочий поток
public class BiggestAsyncSecondWorkerThread implements Runnable {
private AsyncContext context;
public BiggestAsyncSecondWorkerThread(AsyncContext context) {
this.context = context;
}
@Override
public void run() {
//The idea is to write something to the response and then dispatch.
try {
AsyncRequestProcessor.waitingTime(6000);
PrintWriter writer = context.getResponse().getWriter();
context.getRequest().getRequestDispatcher("pages/common/cr_leftnav.jsp").
include(context.getRequest(), context.getResponse());
writer.print(" <div id=\"content-body\">\n" +
" <h3>The external app</h3>");
writer.print("<p>This is the page you have been waiting so patiently for. After one round of asynchronous processing" +
"here you are. I love you..!!</p>");
writer.print(" </div>\n" +
" </div>\n" +
"</div>\n" +
"</body>\n" +
"</html>");
context.getResponse().flushBuffer();
//context.complete();
context.dispatch();
} catch (IOException | ServletException e) {
e.printStackTrace();
}
}
}
И, наконец, первоначальный вызов от JSP, который вызвал этот запрос в первую очередь
<div id="sidebar">
<ul id="parent_nav">
<li><a href="${pageContext.request.contextPath}/biggestAsyncRequestTest">Checking everything async does</a></li>
</ul>
</div>
Примечание: у меня тоже есть асинхронный слушатель. Но ошибка, похоже, не имеет к этому никакого отношения, поэтому оставляя ее
Некоторая дополнительная информация
До того, как ошибка, о которой я упоминал в верхней части страницы, печатается, в журналах печатаются следующие строки, свидетельствующие о том, что в строке 23 второго сервлета происходит ошибка.
<<AsyncRequestLoggingFilter>> This is BEFORE calling the doFilter
Hi I'm the servlet BiggestAsyncFirstServlet and my job is done
<<AsyncRequestLoggingFilter>> This is AFTER returning from the doFilter call
<<AsyncRequestLoggingFilter>> This is BEFORE calling the doFilter during the ASYNC dispatching
Мои извинения за такой длинный вопрос. Любая помощь, которую я могу получить, чтобы понять, почему он говорит: "Фильтр или сервлет текущей цепочки не поддерживает асинхронные операции". Несмотря на то, что все компоненты помечены asyncSupported = true, это очень ценится.
Спасибо, Кингшук