简体   繁体   English

使用AsyncContext的HttpServlet没有响应

[英]No response from HttpServlet with AsyncContext

I'm trying to implement async servlet on Tomcat, that will send update to client every time an HttpSessionAttributeListener.attributeReplaced() is triggered. 我正在尝试在Tomcat上实现异步servlet,每次触发HttpSessionAttributeListener.attributeReplaced()时,它将向客户端发送更新。 Client side is configured to receive Server Sent Events. 客户端配置为接收服务器发送事件。

Although the listener receives the updates, the browser does not receive any response. 尽管侦听器收到更新,但浏览器未收到任何响应。 Browser's developer pane shows, that the request is pending and it ends with error 500 after the timeout set by the AsyncContext.setTimeout() . 浏览器的开发人员窗格显示,该请求正在pending并在AsyncContext.setTimeout()设置的超时后以错误500结尾。 I run out of ideas, why this is happening. 我没有想法,为什么会这样。

JS JS

var source = new EventSource('/acount/sse');

source.onmessage = function (event) {
    console.log(event.data);
    document.querySelector('#messageArea p').innerHTML += event.data;
};

And this is my servlet code: 这是我的servlet代码:

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();
            }

        }
    }


}

The problem: 问题:

I tried your code and there was a java.lang.NullPointerException in: 我尝试了您的代码,并且在其中存在java.lang.NullPointerException:

 aCtx.getResponse().getWriter().write(message);

Because the aCtx is null. 因为aCtx为null。

You have mixed the Servlet and Listener, but when the Listeners methods are called, the AsyncContext initialization is not. 您已经将Servlet和Listener混合使用,但是当调用Listeners方法时,不会初始化AsyncContext。 Therefore nothings go to the browser. 因此,什么也没有进入浏览器。

I splitted your code in Servlet an Listener and smuggled the AsychContext object through a session attribute. 我在Servlet和Listener中拆分了代码,并通过session属性走私了AsychContext对象。 So it was accessible in the listener. 因此可以在侦听器中访问它。

And it works. 而且有效。

The Complete Code: 完整代码:

The HTML: 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>

The Servlet: 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");
    }
}

The HttpSessionAttributeListener: 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();
            }
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM