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}