我试图在Tomcat上实现异步servlet,每次触发HttpSessionAttributeListener.attributeReplaced()
时都会向客户端发送更新。客户端配置为接收服务器已发送事件。
虽然侦听器收到更新,但浏览器不会收到任何响应。浏览器的开发人员窗格显示请求为pending
,并且在500
设置的超时后,它以错误AsyncContext.setTimeout()
结束。我没有想法,为什么会这样。
JS
var source = new EventSource('/acount/sse');
source.onmessage = function (event) {
console.log(event.data);
document.querySelector('#messageArea p').innerHTML += event.data;
};
这是我的servlet代码:
的Servlet
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() + "\n\n";
aCtx.getResponse().getWriter().write(message);
aCtx.getResponse().getWriter().flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
答案 0 :(得分:0)
问题:
我尝试了你的代码,在:
中有一个java.lang.NullPointerException aCtx.getResponse().getWriter().write(message);
因为aCtx为null。
您已经混合了Servlet和Listener,但是当调用Listeners方法时,AsyncContext初始化不是。因此,请访问浏览器。
我将您的代码在Servlet中分割为一个监听器,并通过会话属性走私AsychContext对象。所以它在听众中是可以访问的。
它有效。
完整代码:
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Server Event Listener</title>
</head>
<body>
<div id="messageArea">
<p></p>
</div>
<script>
var source = new EventSource('/yourPath/ServerSentEvent');
source.onmessage = function (event) {
console.log(event.data);
document.querySelector('#messageArea p').innerHTML += event.data;
};
</script>
</body>
</html>
Servlet:
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:
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() + "<br>\n\n";
aCtx.getResponse().getWriter().write(message);
aCtx.getResponse().getWriter().flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}