001package jmri.server.json;
002
003import java.io.IOException;
004import java.net.SocketTimeoutException;
005import jmri.InstanceManager;
006import org.eclipse.jetty.io.EofException;
007import org.eclipse.jetty.websocket.api.Session;
008import org.eclipse.jetty.websocket.api.StatusCode;
009import org.eclipse.jetty.websocket.api.WebSocketException;
010import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
011import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
012import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
013import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
014import org.eclipse.jetty.websocket.api.annotations.WebSocket;
015import org.slf4j.Logger;
016import org.slf4j.LoggerFactory;
017
018/**
019 * @author Randall Wood Copyright (C) 2012, 2013, 2016
020 */
021@WebSocket
022public class JsonWebSocket {
023
024    private static final Logger log = LoggerFactory.getLogger(JsonWebSocket.class);
025    private JsonConnection connection;
026    private JsonClientHandler handler;
027    private Runnable shutDownTask;
028
029    @OnWebSocketConnect
030    public void onOpen(Session sn) {
031        log.debug("Opening connection");
032        try {
033            this.connection = new JsonConnection(sn);
034            sn.setIdleTimeout(
035                    (long) (InstanceManager.getDefault(JsonServerPreferences.class).getHeartbeatInterval() * 1.1));
036            this.handler = new JsonClientHandler(this.connection);
037            this.shutDownTask = () -> {
038                try {
039                    getConnection().sendMessage(
040                            getConnection().getObjectMapper().createObjectNode().put(JSON.TYPE, JSON.GOODBYE), 0);
041                }
042                catch (IOException e) {
043                    log.warn("Unable to send goodbye while closing socket.\nError was {}", e.getMessage());
044                }
045                this.getConnection().getSession().close();
046            };
047            log.debug("Sending hello");
048            this.handler.onMessage(JsonClientHandler.HELLO_MSG);
049        } catch (IOException e) {
050            log.warn("Error opening WebSocket:\n{}", e.getMessage());
051            sn.close();
052        }
053        InstanceManager.getDefault(jmri.ShutDownManager.class).register(this.shutDownTask);
054    }
055
056    @OnWebSocketClose
057    public void onClose(int i, String string) {
058        log.debug("Closing connection because {} ({})", string, i);
059        this.handler.onClose();
060        InstanceManager.getDefault(jmri.ShutDownManager.class).deregister(this.shutDownTask);
061    }
062
063    @OnWebSocketError
064    public void onError(Throwable thrwbl) {
065        if (thrwbl instanceof SocketTimeoutException) {
066            this.connection.getSession().close(StatusCode.NO_CLOSE, thrwbl.getMessage());
067        } else if (thrwbl instanceof EofException || thrwbl instanceof WebSocketException) {
068            try {
069                this.connection.getSession().disconnect();
070            } catch (IOException ex) {
071                this.onClose(StatusCode.ABNORMAL, thrwbl.getMessage());
072            }
073        } else {
074            log.error("Unanticipated error", thrwbl);
075        }
076    }
077
078    @OnWebSocketMessage
079    public void onMessage(String string) {
080        try {
081            this.handler.onMessage(string);
082        } catch (IOException e) {
083            if (!e.getMessage().equals("Will not send message on non-open session")) {
084                // This exception was thrown for some reason other than the
085                // connection is closing or is already closed, so log it.
086                log.error("Error on WebSocket message:\n{}", e.getMessage());
087            }
088            this.connection.getSession().close();
089            InstanceManager.getDefault(jmri.ShutDownManager.class).deregister(this.shutDownTask);
090        }
091    }
092
093    /**
094     * @return the connection
095     */
096    protected JsonConnection getConnection() {
097        return connection;
098    }
099
100}