Tomcat – WebSockets not working on Tomcat 7

tomcattomcat7websocket

I installed Apache Tomcat 7.0.29 on 32bit Windows XP as well as on 64bit Debian 6.0.5 Linux and tried using the websocket examples. But they are not running correctly. I'm not even able to connect to the server.

Using the echo example (choosing messages API) and pressing the Connect button nothing happens. But after 20sec the message "WebSocket connection closed" appears in the log textarea. But as other articles state, this is a known issue.

When using a self-made websocket application and trying to connect to the server, I noticed that the log statements of the MessageInbound#onOpen method are printed, hence this method is called. However, the onopen callback in the browsers Javascript part is not firing. But directly after terminating the tomcat instance, the clients onopen is called, immediately followed by an onclose.

Is there anybody who can confirm this or a similar behavior? Or has anyone got the websocket examples working on Tomcat 7? Thanks for any help.

Update: I added the code for my self created sample application.

Here is the server part, the WebSocketTestServlet.java:

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;

import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;
import org.apache.catalina.websocket.WsOutbound;

@WebServlet(value = "/websocket", loadOnStartup = 1)
public class WebSocketTestServlet extends WebSocketServlet
{
    private static final long serialVersionUID = 1L;

    @Override
    protected StreamInbound createWebSocketInbound(String subProtocol, HttpServletRequest request)
    {
        return new WebSocketTestInbound();
    }

    public static final class WebSocketTestInbound extends MessageInbound
    {
        @Override
        protected void onOpen(WsOutbound outbound)
        {
            System.out.println("WebSocketTestServlet#onOpen");
        }

        @Override
        protected void onClose(int status)
        {
            System.out.println("WebSocketTestServlet#onClose");
        }

        @Override
        protected void onBinaryMessage(ByteBuffer message) throws IOException
        {
            System.out.println("WebSocketTestServlet#onBinaryMessage received: " + message);
        }

        @Override
        protected void onTextMessage(CharBuffer message) throws IOException
        {           
            System.out.println("WebSocketTestServlet#onTextMessage received: " + message);
        }
    }
}

And here the one and only JSF facelet main.xhtml:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html">

    <h:head>
        <title>Test suite</title>
        <script type="text/javascript">

        var ws = null;

        function connect()
        {
            var wsUrl = $('#id_wsUrl');
            var wsMsg = $('#id_wsMsg');
            var wsSnd = $('#id_wsSnd');
            var wsCon = $('#id_wsCon');
            var url = wsUrl.val();

            // Connect
            if (ws == null)
            {
                if (window.WebSocket)
                    ws = new WebSocket(url);
                else if (window.MozWebSocket)
                    ws = new MozWebSocket(url);
                else
                {
                    alert('WebSocket not supported by this browser');
                    return;
                }

                ws.onopen = function()
                {
                    alert('Connection opened!');
                    wsMsg.removeAttr('disabled');
                    wsSnd.removeAttr('disabled');
                    wsUrl.attr('disabled', 'disabled');
                    wsCon.val('Disconnect');
                };

                ws.onclose = function()
                {
                    alert('Connection closed!');
                    wsMsg.attr('disabled', 'disabled');
                    wsSnd.attr('disabled', 'disabled');
                    wsUrl.removeAttr('disabled');
                    wsCon.val('Connect');
                };

                ws.onmessage = function(event)
                {
                    console.log('Inbound message: ' + event.data);
                    alert('Inbound message: ' + event.data);
                };

                ws.onerror = function()
                {
                    alert('Connection error!!!');
                };
            }
            // Disconnect
            else
            {
                ws.close();
                ws = null;
            }
        }

        function sendMessage()
        {
            if (ws)
            {
                var wsMsg = $('#id_wsMsg');
                var data = wsMsg.val();
                wsMsg.val('');

                if (data.length > 0)
                    ws.send(data);
            }
        }

        </script>
    </h:head>
    <h:outputScript target="head" library="js" name="jquery-1.8.0.js" />
    <h:body>
        <h1>WebSocket tests</h1>
        <h:panelGrid columns="2">
            <h:outputLabel for="id_wsUrl" value="WebSocket server URL:" />
            <h:panelGroup>
                <h:inputText id="id_wsUrl" style="width: 250px;" value="ws://localhost:8080/Testapp/websocket" />
                <h:commandButton type="button" id="id_wsCon" value="Connect" onclick="connect();" />
            </h:panelGroup>
            <h:outputLabel for="id_wsMsg" value="WebSocket outbound message" />
            <h:panelGroup>
                <h:inputText id="id_wsMsg" style="width: 250px;" value="" disabled="true" />
                <h:commandButton type="button" id="id_wsSnd" value="Send" disabled="true" onclick="sendMessage();" />
            </h:panelGroup>
        </h:panelGrid>
    </h:body>
</html>

I don't know how, but I hope it helps having the code.

Sebastian

Best Answer

extending WebSocketServlet is now deprecated, even in Tomcat 7. Since Tomcat 7.0.47 the standard javax.websocket-api is provided which means you can easily create a websocket endpoint by using the @ServerEndpoint annotation on your class. Make sure you set the dependency as provided.

Try hitting Tomcat directly to start with as running behind Apache or IIS as a proxy can also cause problems.