1 /* 2 * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package nsk.share.jpda; 24 25 import java.io.IOException; 26 import java.net.InetSocketAddress; 27 import java.net.ServerSocket; 28 import nsk.share.*; 29 30 /* 31 * This class represents communication channel based on TCP/IP sockets. 32 * Usage of this class implies creation of objects of 2 types: server SocketIOPipe object 33 * (this object creates server socket and waits for incoming connection) and client 34 * SocketIOPipe (this object attaches to server). 35 * 36 * Server and client objects should be created using special static methods provided by this class, 37 * for example 'createServerIOPipe(Log log, int port, long timeout)' for server SocketIOPipe 38 * and 'createClientIOPipe(Log log, String host, int port, long timeout)' for client SocketIOPipe. 39 * 40 * When SocketIOPipe is created it can be used to send and receive strings using methods 'readln()' and 'println(String s)'. 41 * TCP/IP connection is established at the first attempt to read or write data. 42 * 43 * For example, if client process should send string 'OK' to the server process which is run 44 * at the host 'SERVER_HOST' following code can be written: 45 * 46 * Server side: 47 * 48 * // SocketIOPipe creates ServerSocket listening given port 49 * SocketIOPipe pipe = SocketIOPipe.createServerIOPipe(log, port, timeoutValue); 50 * 51 * // SocketIOPipe waits connection from client and reads data sent by the client 52 * String command = pipe.readln(); 53 * 54 * Client side: 55 * 56 * // initialize SocketIOPipe with given values of server host name and port 57 * SocketIOPipe pipe = SocketIOPipe.createClientIOPipe(log, 'SERVER_HOST', port, timeoutValue); 58 * 59 * String command = "OK"; 60 * // SocketIOPipe tries to create socket and send command to the server 61 * pipe.println(command); 62 * 63 */ 64 public class SocketIOPipe extends Log.Logger implements Finalizable { 65 66 public static final int DEFAULT_TIMEOUT_VALUE = 1 * 60 * 1000; 67 68 public static final String DEFAULT_PIPE_LOG_PREFIX = "SocketIOPipe> "; 69 70 protected boolean listening; 71 72 protected String host; 73 74 protected int port; 75 76 protected long timeout; 77 78 protected SocketConnection connection; 79 80 protected volatile boolean shouldStop; 81 82 protected Process connectingProcess; 83 84 protected ServerSocket serverSocket; 85 86 protected String name; 87 88 /** 89 * Make general <code>IOPipe</code> object with specified parameters. 90 */ SocketIOPipe(String name, Log log, String logPrefix, String host, int port, long timeout, boolean listening)91 protected SocketIOPipe(String name, Log log, String logPrefix, String host, int port, long timeout, boolean listening) { 92 super(log, logPrefix); 93 this.host = host; 94 this.port = port; 95 this.timeout = timeout; 96 this.listening = listening; 97 this.name = name; 98 } 99 100 /** 101 * Make general <code>IOPipe</code> object with specified parameters. 102 */ SocketIOPipe(Log log, String logPrefix, String host, int port, long timeout, boolean listening)103 protected SocketIOPipe(Log log, String logPrefix, String host, int port, long timeout, boolean listening) { 104 super(log, logPrefix); 105 this.host = host; 106 this.port = port; 107 this.timeout = timeout; 108 this.listening = listening; 109 } 110 111 /** 112 * Create listening SocketIOPipe using given port 113 */ createServerIOPipe(Log log, int port, long timeout)114 public static SocketIOPipe createServerIOPipe(Log log, int port, long timeout) { 115 SocketIOPipe pipe = new SocketIOPipe(log, DEFAULT_PIPE_LOG_PREFIX, null, 0, timeout, true); 116 117 try { 118 ServerSocket ss = new ServerSocket(); 119 if (port == 0) { 120 // Only need SO_REUSEADDR if we're using a fixed port. If we 121 // start seeing EADDRINUSE due to collisions in free ports 122 // then we should retry the bind() a few times. 123 ss.setReuseAddress(false); 124 } 125 ss.bind(new InetSocketAddress(port)); 126 pipe.setServerSocket(ss); 127 } catch (IOException e) { 128 e.printStackTrace(log.getOutStream()); 129 throw new Failure("Caught IOException while binding for IOPipe connection: \n\t" + e); 130 } 131 132 return pipe; 133 } 134 135 /** 136 * Create listening SocketIOPipe using any free port 137 */ createServerIOPipe(Log log, long timeout)138 public static SocketIOPipe createServerIOPipe(Log log, long timeout) { 139 return createServerIOPipe(log, 0, timeout); 140 } 141 142 /** 143 * Create attaching SocketIOPipe using given port and timeout 144 */ createClientIOPipe(Log log, String host, int port, long timeout)145 public static SocketIOPipe createClientIOPipe(Log log, String host, int port, long timeout) { 146 return new SocketIOPipe(log, DEFAULT_PIPE_LOG_PREFIX, host, port, timeout, false); 147 } 148 149 /** 150 * Return true if <code>IOPipe</code> connection established. 151 */ isConnected()152 public boolean isConnected() { 153 return (connection != null && connection.isConnected()); 154 } 155 156 /** 157 * Returns port number used by SocketIOPipe 158 */ getPort()159 public int getPort() { 160 return port; 161 } 162 setConnectingProcess(Process connectingProcess)163 protected void setConnectingProcess(Process connectingProcess) { 164 this.connectingProcess = connectingProcess; 165 } 166 setServerSocket(ServerSocket serverSocket)167 protected void setServerSocket(ServerSocket serverSocket) { 168 this.serverSocket = serverSocket; 169 if (serverSocket != null) 170 port = serverSocket.getLocalPort(); 171 } 172 173 /** 174 * Write (and flush) given <code>line</code> to this 175 * <code>IOPipe</code> cnannel. 176 * 177 * @throws Failure if error occured while sending data 178 */ println(String line)179 public void println(String line) { 180 if (connection == null) { 181 connect(); 182 } 183 connection.writeObject(line); 184 } 185 186 /** 187 * Read a text line from this <code>IOPipe</code> channel, 188 * or return <i>null</i> if EOF reached. 189 * 190 * @throws Failure if error occured while reading data 191 */ readln()192 public String readln() { 193 if (connection == null) { 194 connect(); 195 } 196 String line = (String) connection.readObject(); 197 return line; 198 } 199 200 /** 201 * Close this <code>IOPipe</code> connection. 202 */ close()203 public void close() { 204 shouldStop = true; 205 if (connection != null) { 206 connection.close(); 207 } 208 } 209 210 /** 211 * Establish <code>IOPipe</code> connection by attaching or accepting 212 * connection appropriately. 213 */ connect()214 protected void connect() { 215 if (connection != null) { 216 throw new TestBug("IOPipe connection is already established"); 217 } 218 219 if (shouldStop) 220 return; 221 222 connection = new SocketConnection(this, getName()); 223 224 if (listening) { 225 connection.setConnectingProcess(connectingProcess); 226 if (serverSocket == null) { 227 connection.bind(port, timeout); 228 } else { 229 connection.setServerSocket(serverSocket); 230 } 231 232 if (shouldStop) 233 return; 234 235 // wait for connection from remote host 236 connection.accept(timeout); 237 238 } else { 239 // attach from the debuggee's side 240 connection.continueAttach(host, port, timeout); 241 } 242 } 243 244 /** 245 * Set ping timeout in milliseconds (0 means don't use ping at all). 246 */ setPingTimeout(long timeout)247 public void setPingTimeout(long timeout) { 248 if (connection == null) { 249 throw new TestBug("Attempt to set ping timeout for not established connection"); 250 } 251 connection.setPingTimeout(timeout); 252 } 253 254 /** 255 * Returns value of current ping timeout in milliseconds (0 means ping is not used). 256 */ getPingTimeout()257 public long getPingTimeout() { 258 if (connection == null) { 259 throw new TestBug("Attempt to get ping timeout for not established connection"); 260 } 261 return connection.getPingTimeout(); 262 } 263 264 /** 265 * Perform finalization of the object by invoking close(). 266 */ finalize()267 protected void finalize() throws Throwable { 268 close(); 269 super.finalize(); 270 } 271 272 /** 273 * Perform finalization of the object at exit by invoking finalize(). 274 */ finalizeAtExit()275 public void finalizeAtExit() throws Throwable { 276 finalize(); 277 } 278 279 /** 280 * Field 'pipeCounter' and method 'getNextPipeNumber' are used to construct unique names for SocketIOPipes 281 */ 282 private static int pipeCounter; 283 getNextPipeNumber()284 private synchronized int getNextPipeNumber() { 285 return pipeCounter++; 286 } 287 288 /** 289 * Construct name for SocketIOPipe if it wasn't specified 290 */ getName()291 private String getName() { 292 if (name == null) { 293 name = "SocketIOPipe-" + getNextPipeNumber(); 294 } 295 296 return name; 297 } 298 } 299