2

I try to implement Server-Sent-Event in my Webapp with Java Serlvet on server.

Is it possible to check in Servlet that connection is closed by client? The loop while(true) in Servlet is infinite even if client browser is closed.

Client code

    function startLogSSE(lastEventId, level) {
        var eventSource = new EventSource("log-sse?last-event-id=" + lastEventId + "&level=" + level);
        eventSource.onmessage = function (event) {
            document.getElementById('log').innerHTML = event.data + "\n" + document.getElementById('log').innerHTML;
        };
    }

Server code

public class LogSSEServlet extends HttpServlet {
    private static final Logger logger = LoggerFactory.getLogger(LogSSEServlet.class);

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/event-stream");
        response.setCharacterEncoding("UTF-8");

        PrintWriter writer = response.getWriter();

        // get logger purgerDB appender
        PurgerDBAppender appender = LogUtils.getPurgerDBAppender();
        if (appender == null) {
            writer.write("data: [ERROR] Appender 'purgerDB' isn't found for logger 'com.bp3'\n\n");
            writer.close();
            return;
        }

        int eventId = 0;
        // get last-event-id
        String lastEventId = request.getHeader("last-event-id");
        if (lastEventId == null) {
            // try to get lastEventId from parameter
            lastEventId = request.getParameter("last-event-id");
        }
        if (lastEventId != null) {
            try {
                eventId = Integer.parseInt(lastEventId);
            } catch (NumberFormatException e) {
                logger.error("Failed to parse last-event-id: " + lastEventId);
            }
        }
        String minLevel = request.getParameter("level");
        if (minLevel == null) {
            minLevel = "TRACE";
        }

        // get logs from purgerDB logger appender
        LogServices logServices = new LogServices();
        try {
            logServices.open();
        } catch (SQLException e) {
            throw new ServletException(e);
        }
        try {
            while (true) {
                List<LogMessage> messages = logServices.getLastMessages(Level.toLevel(minLevel), eventId, 0);
                if (messages.size() > 0) {
                    writer.write("id: " + messages.get(0).getEventId() + "\n");
                    writer.write("data: " + LogUtils.formatLog(messages) + "\n");
                    writer.flush();
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    break;
                }
            }
        } catch (SQLException e) {
            throw new ServletException(e);
        } finally {
            logServices.closeQuietly();
        }
    }
}
JasonMArcher
  • 14,195
  • 22
  • 56
  • 52
sasha_trn
  • 1,935
  • 2
  • 23
  • 30
  • Seems to me like you're trying to reimplement WebSockets. Or Comet. – kaqqao Aug 29 '14 at 18:58
  • Do some googling on 'persistent connections' if you're trying to keep it open, otherwise by default I believe the connection should be closed once all data has been transmitted by the server. For creating a persistent connection see http://stackoverflow.com/questions/3304006/persistent-httpurlconnection-in-java – Jason Sep 02 '14 at 23:38

2 Answers2

0

Is it possible to check in Servlet that connection is closed by client?

Eventually an exception will be thrown: either an IOException: connection reset if you are streaming directly to the socket, or an OutOfMemoryError if the container is streaming to memory, which it does when you aren't using a fixed-length or chunked transfer mode.

The loop while(true) in Servlet is infinite even if client browser is closed.

No it isn't.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • 1
    Yes, it is :). No exception is thrown. I also expected it. Application server used: WebSphere Application Server Liberty Profile – sasha_trn Sep 06 '14 at 21:01
  • hi, @sasha_trn, could you share with us your conclusions on this issue? Thanks! – Ricardo Jul 08 '15 at 23:47
  • 1
    Hi @Ricardo, It was a long time ago. AFAIR, I didn't find a way to determine connection closing by client. I changed my code without the infinite loop. – sasha_trn Jul 09 '15 at 13:26
  • Thanks, @sasha_trn, from `HttpServlet`, `HttpServletRequest` and `HttpServletResponse` [source codes](http://grepcode.com/), I see no mention to connection, and also I get no `Exception`. Best, – Ricardo Jul 09 '15 at 17:35
0

One way to check, wihin the Servlet, that connection is closed, is using the writer.checkError() method. I tested this fix on Chrome and it works. Your code would be:

            boolean error=false;
            while (!error) {
                //...                   
                writer.write("data: " + /*...*/  "\n");
                //writer.flush();
                error = writer.checkError();    //internally calls writer.flush()
            }

Details:

The PrintWriter's API says:

Methods in this class never throw I/O exceptions, although some of its constructors may. The client may inquire as to whether any errors have occurred by invoking checkError().

and the checkError() says:

Flushes the stream if it's not closed and checks its error state

Ricardo
  • 3,696
  • 5
  • 36
  • 50