001package jmri.jmrix; 002 003import java.io.DataInputStream; 004import java.io.DataOutputStream; 005import java.io.IOException; 006import java.net.Socket; 007 008import jmri.SystemConnectionMemo; 009 010/** 011 * Enables basic setup of a network client interface for a jmrix implementation. 012 * 013 * @author Kevin Dickerson Copyright (C) 2010 014 * @author Based upon work originally done by Paul Bender Copyright (C) 2009 015 * @see jmri.jmrix.NetworkConfigException 016 */ 017abstract public class AbstractNetworkPortController extends AbstractPortController implements NetworkPortAdapter { 018 019 // the host name and port number identify what we are talking to. 020 protected String m_HostName = null; 021 private String m_HostAddress = null; // Internal IP address for ZeroConf 022 // configured clients. 023 protected int m_port = 0; 024 // keep the socket provides our connection. 025 protected Socket socketConn = null; 026 protected int connTimeout = 0; // connection timeout for read operations. 027 // Default is 0, an infinite timeout. 028 029 protected AbstractNetworkPortController(SystemConnectionMemo connectionMemo) { 030 super(connectionMemo); 031 setHostName(""); // give the host name a default value of the empty string. 032 } 033 034 @Override 035 public void connect(String host, int port) throws IOException { 036 setHostName(host); 037 setPort(port); 038 connect(); 039 } 040 041 @Override 042 public void connect() throws IOException { 043 log.debug("connect() starts to {}:{}", getHostName(), getPort()); 044 opened = false; 045 if (getHostAddress() == null || m_port == 0) { 046 log.error("No host name or port set: {}:{}", m_HostName, m_port); 047 return; 048 } 049 try { 050 socketConn = new Socket(getHostAddress(), m_port); 051 socketConn.setKeepAlive(true); 052 socketConn.setSoTimeout(getConnectionTimeout()); 053 opened = true; 054 } catch (IOException e) { 055 log.error("Error opening network connection to {} because {}", getHostName(), e.getMessage()); // nothing to help user in full exception 056 if (m_port != 0) { 057 ConnectionStatus.instance().setConnectionState( 058 getUserName(), m_HostName + ":" + m_port, ConnectionStatus.CONNECTION_DOWN); 059 } else { 060 ConnectionStatus.instance().setConnectionState( 061 getUserName(), m_HostName, ConnectionStatus.CONNECTION_DOWN); 062 } 063 throw (e); 064 } 065 if (opened && m_port != 0) { 066 ConnectionStatus.instance().setConnectionState( 067 getUserName(), m_HostName + ":" + m_port, ConnectionStatus.CONNECTION_UP); 068 } else if (opened) { 069 ConnectionStatus.instance().setConnectionState( 070 getUserName(), m_HostName, ConnectionStatus.CONNECTION_UP); 071 } 072 log.trace("connect ends"); 073 } 074 075 /** 076 * Remember the associated host name. 077 * 078 * @param s the host name; if empty will use MDNS to get host name 079 */ 080 @Override 081 public void setHostName(String s) { 082 log.trace("setHostName({})", s, new Exception("traceback only")); 083 m_HostName = s; 084 if ((s == null || s.equals("")) && !getMdnsConfigure()) { 085 m_HostName = JmrixConfigPane.NONE; 086 } 087 } 088 089 @Override 090 public String getHostName() { 091 return m_HostName; 092 } 093 094 /** 095 * Remember the associated IP Address This is used internally for mDNS 096 * configuration. Public access to the IP address is through the hostname 097 * field. 098 * 099 * @param s the address; if empty, will use the host name 100 */ 101 protected void setHostAddress(String s) { 102 log.trace("setHostAddress({})", s); 103 m_HostAddress = s; 104 if (s == null || s.equals("")) { 105 m_HostAddress = m_HostName; 106 } 107 } 108 109 protected String getHostAddress() { 110 if (m_HostAddress == null) { 111 return m_HostName; 112 } 113 return m_HostAddress; 114 } 115 116 /** 117 * Remember the associated port number. 118 * 119 * @param p the port 120 */ 121 @Override 122 public void setPort(int p) { 123 log.trace("setPort(int {})", p); 124 m_port = p; 125 } 126 127 @Override 128 public void setPort(String p) { 129 log.trace("setPort(String {})", p); 130 m_port = Integer.parseInt(p); 131 } 132 133 @Override 134 public int getPort() { 135 return m_port; 136 } 137 138 /** 139 * Return the connection name for the network connection in the format of 140 * ip_address:port 141 * 142 * @return ip_address:port 143 */ 144 @Override 145 public String getCurrentPortName() { 146 String t; 147 if (getMdnsConfigure()) { 148 t = getHostAddress(); 149 } else { 150 t = getHostName(); 151 } 152 int p = getPort(); 153 if (t != null && !t.equals("")) { 154 if (p != 0) { 155 return t + ":" + p; 156 } 157 return t; 158 } else { 159 return JmrixConfigPane.NONE; 160 } 161 } 162 163 /* 164 * Set whether or not this adapter should be 165 * configured automatically via MDNS. 166 * Note: Default implementation ignores the parameter. 167 * 168 * @param autoconfig boolean value 169 */ 170 @Override 171 public void setMdnsConfigure(boolean autoconfig) { 172 } 173 174 /* 175 * Get whether or not this adapter is configured 176 * to use autoconfiguration via MDNS 177 * Default implemntation always returns false. 178 * 179 * @return true if configured using MDNS 180 */ 181 @Override 182 public boolean getMdnsConfigure() { 183 return false; 184 } 185 186 /* 187 * Set the server's host name and port 188 * using MDNS autoconfiguration. 189 * Default implementation does nothing. 190 */ 191 @Override 192 public void autoConfigure() { 193 } 194 195 /* 196 * Get and set the ZeroConf/mDNS advertisement name. 197 * Default implementation does nothing. 198 */ 199 @Override 200 public void setAdvertisementName(String AdName) { 201 } 202 203 @Override 204 public String getAdvertisementName() { 205 return null; 206 } 207 208 /* 209 * Get and set the ZeroConf/mDNS service type. 210 * Default implementation does nothing. 211 */ 212 @Override 213 public void setServiceType(String ServiceType) { 214 } 215 216 @Override 217 public String getServiceType() { 218 return null; 219 } 220 221 /** 222 * {@inheritDoc} 223 */ 224 @Override 225 public DataInputStream getInputStream() { 226 log.trace("getInputStream() starts"); 227 if (socketConn == null) { 228 log.error("getInputStream invoked with null socketConn"); 229 } 230 if (!opened) { 231 log.error("getInputStream called before load(), stream not available"); 232 if (m_port != 0) { 233 ConnectionStatus.instance().setConnectionState( 234 getUserName(), m_HostName + ":" + m_port, ConnectionStatus.CONNECTION_DOWN); 235 } else { 236 ConnectionStatus.instance().setConnectionState( 237 getUserName(), m_HostName, ConnectionStatus.CONNECTION_DOWN); 238 } 239 } 240 try { 241 log.trace("getInputStream() returns normally"); 242 return new DataInputStream(socketConn.getInputStream()); 243 } catch (java.io.IOException ex1) { 244 log.error("Exception getting input stream:", ex1); 245 return null; 246 } 247 } 248 249 /** 250 * {@inheritDoc} 251 */ 252 @Override 253 public DataOutputStream getOutputStream() { 254 if (!opened) { 255 log.error("getOutputStream called before load(), stream not available"); 256 } 257 try { 258 return new DataOutputStream(socketConn.getOutputStream()); 259 } catch (java.io.IOException e) { 260 log.error("getOutputStream exception:", e); 261 if (m_port != 0) { 262 ConnectionStatus.instance().setConnectionState( 263 getUserName(), m_HostName + ":" + m_port, ConnectionStatus.CONNECTION_DOWN); 264 } else { 265 ConnectionStatus.instance().setConnectionState( 266 getUserName(), m_HostName, ConnectionStatus.CONNECTION_DOWN); 267 } 268 } 269 return null; 270 } 271 272 /** 273 * {@inheritDoc} 274 */ 275 @Override 276 protected void closeConnection(){ 277 try { 278 socketConn.close(); 279 } catch (IOException e) { 280 log.trace("Unable to close socket", e); 281 } 282 opened=false; 283 } 284 285 /** 286 * Customizable method to deal with resetting a system connection after a 287 * successful recovery of a connection. 288 */ 289 @Override 290 protected void resetupConnection() { 291 } 292 293 /** 294 * {@inheritDoc} 295 */ 296 @Override 297 protected void reconnectFromLoop(int retryNum){ 298 try { 299 // if the device allows autoConfiguration, 300 // we need to run the autoConfigure() call 301 // before we try to reconnect. 302 if (getMdnsConfigure()) { 303 autoConfigure(); 304 } 305 connect(); 306 } catch (IOException ex) { 307 log.trace("restart failed", ex); // main warning to log.error done within connect(); 308 // if returned on exception stops thread and connection attempts 309 } 310 } 311 312 /* 313 * Set the connection timeout to the specified value. 314 * If the socket is not null, set the SO_TIMEOUT option on the 315 * socket as well. 316 * 317 * @param t timeout value in milliseconds 318 */ 319 protected void setConnectionTimeout(int t) { 320 connTimeout = t; 321 try { 322 if (socketConn != null) { 323 socketConn.setSoTimeout(getConnectionTimeout()); 324 } 325 } catch (java.net.SocketException se) { 326 log.debug("Unable to set socket timeout option on socket"); 327 } 328 } 329 330 /* 331 * Get the connection timeout value. 332 * 333 * @return timeout value in milliseconds 334 */ 335 protected int getConnectionTimeout() { 336 return connTimeout; 337 } 338 339 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractNetworkPortController.class); 340 341}