001package jmri.jmris; 002 003import java.io.DataOutputStream; 004import java.io.IOException; 005import java.util.Locale; 006 007import org.eclipse.jetty.websocket.api.RemoteEndpoint; 008import org.eclipse.jetty.websocket.api.Session; 009import org.eclipse.jetty.websocket.api.WebSocketException; 010import org.slf4j.Logger; 011import org.slf4j.LoggerFactory; 012 013/** 014 * Abstraction of DataOutputStream and WebSocket.Connection classes. 015 * <p> 016 * Used so that that server objects need only to use a single object/method to 017 * send data to any supported object type. 018 * 019 * @author Randall Wood Copyright (C) 2012, 2014 020 */ 021public class JmriConnection { 022 023 private final Session session; 024 private final DataOutputStream dataOutputStream; 025 private Locale locale = Locale.getDefault(); 026 private static final Logger log = LoggerFactory.getLogger(JmriConnection.class); 027 private static final String EX_SENDING_MSG = "Exception sending message"; 028 029 /** 030 * Create a JmriConnection that sends output to a WebSocket. 031 * 032 * @param connection WebSocket Session to use. 033 */ 034 public JmriConnection(Session connection) { 035 this.session = connection; 036 this.dataOutputStream = null; 037 } 038 039 /** 040 * Create a JmriConnection that sends output to a DataOutputStream. 041 * 042 * @param output DataOutputStream to use 043 */ 044 public JmriConnection(DataOutputStream output) { 045 this.dataOutputStream = output; 046 this.session = null; 047 } 048 049 /** 050 * Get the WebSocket session. 051 * 052 * @return the WebSocket session 053 */ 054 public Session getSession() { 055 return this.session; 056 } 057 058 public DataOutputStream getDataOutputStream() { 059 return dataOutputStream; 060 } 061 062 /** 063 * Send a String to the instantiated connection. 064 * <p> 065 * This method throws an IOException so the server or servlet holding the 066 * connection open can respond to the exception if there is an immediate 067 * failure. If there is an asynchronous failure, the connection is closed. 068 * 069 * @param message message to send 070 * @throws IOException if problem sending message 071 */ 072 public void sendMessage(String message) throws IOException { 073 log.trace("Sending \"{}\"", message); 074 if (this.dataOutputStream != null) { 075 this.dataOutputStream.writeBytes(message); 076 } else if (this.session.isOpen()) { 077 try { 078 RemoteEndpoint remote = this.session.getRemote(); 079 // The JSON sockets keep an internal state variable and throw an 080 // IllegalStateException if more than one thread attempts to do 081 // sendString at the same time. This function gets normally 082 // called from a mixture of the Layout thread and the 083 // WebServer-NN threads. 084 synchronized (remote) { 085 remote.sendString(message); 086 } 087 } catch (WebSocketException ex) { 088 // A WebSocketException is most likely a broken socket, 089 // so rethrow it as an IOException 090 if (ex.getMessage() == null) { 091 // provide a generic message if ex has no message 092 throw new IOException(EX_SENDING_MSG, ex); 093 } 094 throw new IOException(ex); 095 } catch (IOException ex) { 096 if (ex.getMessage() == null) { 097 // provide a generic message if ex has no message 098 throw new IOException(EX_SENDING_MSG, ex); 099 } 100 throw ex; // rethrow if complete 101 } 102 } else { 103 // immediately thrown an IOException to trigger closing 104 // actions up the call chain 105 throw new IOException("Will not send message on non-open session"); 106 } 107 } 108 109 /** 110 * Close the connection. 111 * <p> 112 * Note: Objects using JmriConnection with a 113 * {@link org.eclipse.jetty.websocket.api.Session} may prefer to use 114 * <code>getSession().close()</code> since Session.close() does not throw an 115 * IOException. 116 * 117 * @throws IOException if problem closing connection 118 */ 119 public void close() throws IOException { 120 if (this.dataOutputStream != null) { 121 this.dataOutputStream.close(); 122 } else if (this.session != null) { 123 this.session.close(); 124 } 125 } 126 127 /** 128 * @return the locale 129 */ 130 public Locale getLocale() { 131 return locale; 132 } 133 134 /** 135 * @param locale the locale to set 136 */ 137 public void setLocale(Locale locale) { 138 this.locale = locale; 139 } 140}