No response from HttpServlet with AsyncContext
我正在尝试在Tomcat上实现异步servlet,每次
尽管侦听器收到了更新,但浏览器未收到任何响应。浏览器的开发人员窗格显示,请求为
JS
1 2 3 4 5 6 | var source = new EventSource('/acount/sse'); source.onmessage = function (event) { console.log(event.data); document.querySelector('#messageArea p').innerHTML += event.data; }; |
这是我的servlet代码:
Servlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | public class SSE extends HttpServlet implements HttpSessionAttributeListener { public static final String ATTR_ENTRY_PROCESSOR_PROGRESS ="entryProcessorProgress"; private AsyncContext aCtx; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true); resp.setContentType("text/event-stream"); resp.setHeader("Cache-Control","no-cache"); resp.setHeader("Connection","keep-alive"); resp.setCharacterEncoding("UTF-8"); aCtx = req.startAsync(req, resp); aCtx.setTimeout(80000); } @Override public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) { write(httpSessionBindingEvent); } @Override public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) { } @Override public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) { write(httpSessionBindingEvent); } private void write(HttpSessionBindingEvent httpSessionBindingEvent) { if (httpSessionBindingEvent.getName().equals(ATTR_ENTRY_PROCESSOR_PROGRESS)) { try { String message ="data:" + httpSessionBindingEvent.getValue() +"\ \ "; aCtx.getResponse().getWriter().write(message); aCtx.getResponse().getWriter().flush(); } catch (IOException e) { e.printStackTrace(); } } } } |
问题:
我尝试了您的代码,并且在以下位置出现了java.lang.NullPointerException:
1 | aCtx.getResponse().getWriter().write(message); |
因为aCtx为空。
您已经混合使用了Servlet和Listener,但是在调用Listeners方法时,不会进行AsyncContext初始化。因此,什么也没有进入浏览器。
我在Servlet和侦听器中拆分了您的代码,并通过会话属性走私了AsychContext对象。因此可以在侦听器中访问它。
它有效。
完整代码:
HTML:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> Server Event Listener </head> <body> var source = new EventSource('/yourPath/ServerSentEvent'); source.onmessage = function (event) { console.log(event.data); document.querySelector('#messageArea p').innerHTML += event.data; }; </body> </html> |
Servlet:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | package testingThings.ServerSentEvent; import java.io.IOException; import javax.servlet.AsyncContext; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(asyncSupported = true, value = {"/ServerSentEvent"}) public class ServerSentEvent extends HttpServlet { private static final long serialVersionUID = 1L; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // add @WebServlet(asyncSupported = true) instead // http://stackoverflow.com/questions/7855712/how-to-avoid-request-set-async-supported-true-to-enable-async-servlet-3-0-proces // req.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true); resp.setContentType("text/event-stream"); resp.setHeader("Cache-Control","no-cache"); resp.setHeader("Connection","keep-alive"); resp.setCharacterEncoding("UTF-8"); AsyncContext aCtx = req.startAsync(req, resp); aCtx.setTimeout(80000); // add a asyncContext a session Attribute req.getSession().setAttribute("asyncContext", aCtx); //start logging in listener req.getSession().setAttribute("entryProcessorProgress","trigger output"); } } |
HttpSessionAttributeListener:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | package testingThings.ServerSentEvent; import java.io.IOException; import javax.servlet.AsyncContext; import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionBindingEvent; @WebListener public class SessionAttributeListener implements HttpSessionAttributeListener { public static final String ATTR_ENTRY_PROCESSOR_PROGRESS ="entryProcessorProgress"; @Override public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) { write(httpSessionBindingEvent); } @Override public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) { } @Override public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) { write(httpSessionBindingEvent); } private void write(HttpSessionBindingEvent httpSessionBindingEvent) { if (httpSessionBindingEvent.getName().equals(ATTR_ENTRY_PROCESSOR_PROGRESS)) { try { // get the AsyncContext from the session AsyncContext aCtx = (AsyncContext) httpSessionBindingEvent.getSession().getAttribute("asyncContext"); String message ="data:" + httpSessionBindingEvent.getValue() +"\ \ "; aCtx.getResponse().getWriter().write(message); aCtx.getResponse().getWriter().flush(); } catch (IOException e) { e.printStackTrace(); } } } } |