Path: blob/master/test/hotspot/jtreg/vmTestbase/nsk/share/jpda/SocketConnection.java
41161 views
/*1* Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223package nsk.share.jpda;2425import java.io.*;26import java.net.*;2728import nsk.share.*;2930/**31* This class implements basic connection channel via TCP/IP sockets.32*/33class BasicSocketConnection {3435protected static int TRACE_LEVEL_PACKETS = 10;3637protected static int TRACE_LEVEL_THREADS = 20;3839protected static int TRACE_LEVEL_ACTIONS = 30;4041protected static int TRACE_LEVEL_SOCKETS = 40;4243protected static int TRACE_LEVEL_IO = 50;4445protected String name = null;4647protected ServerSocket serverSocket = null;4849protected Socket socket = null;5051protected InputStream sin = null;5253protected OutputStream sout = null;5455protected Process connectingProcess = null;5657protected volatile boolean connected = false;5859protected volatile boolean closed = false;6061protected volatile boolean connectionClosed = false;6263protected volatile boolean shouldStop = false;6465protected Log.Logger logger = null;6667/**68* Make an empty connection with specified name.69*70* @param logger71* Logger object for printing log messages72* @param name73* connection name74*/75public BasicSocketConnection(Log.Logger logger, String name) {76this.logger = logger;77this.name = name;78}7980/**81* Try to bind connection to the local port.82*83* @param port84* port number to bind to85*86* @throws IOException87* if error occured while binding88*/89protected void tryBind(int port) throws IOException {90logger.trace(TRACE_LEVEL_IO, "Binding for " + name + " connection to port: " + port);91serverSocket = new ServerSocket(port, 1);92logger.trace(TRACE_LEVEL_IO, "Bound for " + name + " connection to port: " + port);93}9495/**96* Bind connection to the local port for specified timeout.97*98* @param port99* port number to bind to100* @param timeout101* binding timeout in milliseconds102*103* @throws Failure104* if error ocured while binding105*/106protected void bind(int port, long timeout) {107BindException bindException = null;108long timeToFinish = System.currentTimeMillis() + timeout;109for (long i = 0; !shouldStop && (timeout == 0 || System.currentTimeMillis() < timeToFinish); i++) {110try {111tryBind(port);112return;113} catch (BindException e) {114bindException = e;115logger.display("Attempt #" + i + " to bind to port " + port + " failed:\n\t" + e);116try {117Thread.sleep(DebugeeBinder.CONNECT_TRY_DELAY);118} catch (InterruptedException ie) {119ie.printStackTrace(logger.getOutStream());120throw new Failure("Thread interrupted while binding for " + name + " connection to port " + port + ":\n\t" + ie);121}122} catch (IOException e) {123e.printStackTrace(logger.getOutStream());124throw new Failure("Caught IOException while binding for " + name + " connection to port " + port + ":\n\t" + e);125}126}127throw new Failure("Unable to bind for " + name + " connection to port " + port + " for " + timeout + "ms timeout:\n\t" + bindException);128}129130/**131* Accept connection at the bound port for specified timeout.132*133* @param timeout134* accepting timeout in milliseconds135*136* @throws Failure137* if error occured while accepting connection138*/139public void accept(long timeout) {140int port = serverSocket.getLocalPort();141logger.trace(TRACE_LEVEL_IO, "Listening for " + name + " connection at port: " + port);142socket = null;143try {144if (timeout > Integer.MAX_VALUE) {145throw new TestBug("Too large timeout long value: " + timeout + " (can't cast it to int)");146}147148serverSocket.setSoTimeout((int)timeout);149150long waitStartTime = System.currentTimeMillis();151152/*153* We found that sometimes (very rarely) on Solaris ServerSocket.accept() throws InterruptedIOException154* even if connection timeout (specified through ServerSocket.setSoTimeout) didn't expire.155* Following code tries to catch such case and call ServerSocket.accept() while timeout didn't expire.156*/157do {158try {159socket = serverSocket.accept();160logger.trace(TRACE_LEVEL_IO, "Accepted " + name + " connection at port: " + port);161} catch (InterruptedIOException e) {162long interruptTime = System.currentTimeMillis();163long waitTime = interruptTime - waitStartTime;164165logger.display("Caught InterruptedIOException. Wait start time: " + waitStartTime + ", exception was thrown at: "166+ interruptTime + ", wait time: " + (interruptTime - waitStartTime) + ", actual timeout: " + timeout);167168// if waitTime was too small call ServerSocket.accept() one more time169if (!shouldStop && (waitTime < (timeout / 2))) {170logger.display("InterruptedIOException was thrown too early, trying to call ServerSocket.accept() one more time");171continue;172} else {173if (!shouldStop) {174logger.complain("Caught InterruptedIOException while listening for " + name + " connection at port " + port + ":\n\t" + e);175throw new Failure("Connection for " + name +176" at port " + port +177" wasn't accepted in " + timeout + "ms");178} else {179logger.display("Listening was interrupted (caught InterruptedIOException while listening for " + name + " connection at port " + port + ":\n\t" + e + ")");180break;181}182}183}184} while (socket == null);185186} catch (IOException e) {187if (!shouldStop) {188e.printStackTrace(logger.getOutStream());189throw new Failure("Caught IOException while listening for " + name + " connection at port " + port + ":\n\t" + e);190} else {191logger.display("Listening was interrupted (caught InterruptedIOException while listening for " + name + " connection at port " + port + ":\n\t" + e + ")");192}193} finally {194closeServerConnection();195}196197if (!shouldStop) {198if (socket == null) {199throw new Failure("No " + name + " connection accepted at port " + port + " for " + timeout + "ms timeout");200}201202onConnected();203}204}205206/**207* Attach connection to the remote host and port.208*209* @param host210* name of remote host to attach to211* @param port212* port number to attach to213*214* @throws Failure215* if error occured while attaching216*/217public void attach(String host, int port) {218try {219logger.trace(TRACE_LEVEL_IO, "Attaching for " + name + " connection to host: " + host + ":" + port);220socket = new Socket(host, port);221socket.setTcpNoDelay(true);222logger.trace(TRACE_LEVEL_IO, "Attached for " + name + " connection to host: " + host + ":" + port);223} catch (IOException e) {224e.printStackTrace(logger.getOutStream());225throw new Failure("Caught IOException while attaching for " + name + " connection to " + host + ":" + port + ":\n\t" + e);226}227if (!shouldStop) {228onConnected();229}230}231232/**233* Continuously attach to the remote host for the specified timeout.234*235* @param host236* name of remote host to attach to237* @param port238* port number to attach to239* @param timeout240* attaching timeout in milliseconds241*242* @throws Failure243* if error occured while attaching244*/245public void continueAttach(String host, int port, long timeout) {246socket = null;247long timeToFinish = System.currentTimeMillis() + timeout;248ConnectException lastException = null;249logger.trace(TRACE_LEVEL_IO, "Attaching for " + name + " connection to host: " + host + ":" + port);250try {251for (long i = 0; !shouldStop && (timeout == 0 || System.currentTimeMillis() < timeToFinish); i++) {252try {253socket = new Socket(host, port);254logger.trace(TRACE_LEVEL_IO, "Attached for " + name + " connection to host: " + host + ":" + port);255break;256} catch (ConnectException e) {257logger.display("Attempt #" + i + " to attach for " + name + " connection failed:\n\t" + e);258lastException = e;259// check if listening process still alive260if (!checkConnectingProcess()) {261shouldStop = true;262throw new Failure("Break attaching to " + name + " connection: " + "listening process exited");263}264// sleep between attempts265try {266Thread.sleep(DebugeeBinder.CONNECT_TRY_DELAY);267} catch (InterruptedException ie) {268throw new Failure("Thread interrupted while attaching for " + name + " connection to " + host + ":" + port + ":\n\t" + ie);269}270}271}272273} catch (IOException e) {274e.printStackTrace(logger.getOutStream());275throw new Failure("Caught IOException while attaching for " + name + " connection to " + host + ":" + port + ":\n\t" + e);276}277278if (!shouldStop) {279if (socket == null) {280throw new Failure("Unable to attach for " + name + " connection to " + host + ":" + port + " for " + timeout + "ms timeout:\n\t"281+ lastException);282}283284onConnected();285}286}287288/**289* Set already bound serverSocket for further connection.290*/291public void setServerSocket(ServerSocket serverSocket) {292this.serverSocket = serverSocket;293}294295/**296* Set already connected socket for connection.297*/298public void setSocket(Socket socket) {299this.socket = socket;300if (!shouldStop) {301onConnected();302}303}304305/**306* Get socket of already established connection.307*/308public Socket getSocket() {309return socket;310}311312/**313* Return true if another connecting process is still alive.314*/315public boolean checkConnectingProcess() {316if (connectingProcess == null) {317// no process to check318return true;319}320try {321int exitCode = connectingProcess.exitValue();322} catch (IllegalThreadStateException e) {323// process is still alive324return true;325}326// process exited327return false;328}329330/**331* Set another connecting process to control if it is still alive.332*/333public void setConnectingProcess(Process process) {334connectingProcess = process;335}336337/**338* Check if connection is established.339*/340public boolean isConnected() {341return connected;342}343344/**345* Close socket and associated streams.346*/347public void close() {348if (!closed) {349shouldStop = true;350closeConnection();351closed = true;352}353}354355/**356* Send the specified byte throw the connection.357*/358public void writeByte(byte b) throws IOException {359logger.trace(TRACE_LEVEL_IO, "Writing byte: " + b);360sout.write(b);361sout.flush();362logger.trace(TRACE_LEVEL_IO, "Wrote byte: " + b);363}364365/**366* Read a byte and return it or -1.367*/368public int readByte() throws IOException {369logger.trace(TRACE_LEVEL_IO, "Reading byte");370int b = sin.read();371logger.trace(TRACE_LEVEL_IO, "Received byte: " + b);372return b;373}374375/**376* Perform some actions after connection established.377*/378protected void onConnected() {379if (!shouldStop) {380setSocketOptions();381makeSocketStreams();382connected = true;383}384}385386/**387* Set socket options after connection established.388*/389protected void setSocketOptions() {390}391392/**393* Close server socket.394*/395protected void closeServerConnection() {396if (serverSocket != null) {397try {398serverSocket.close();399logger.trace(TRACE_LEVEL_IO, "ServerSocket closed: " + serverSocket);400} catch (IOException e) {401logger.display("# WARNING: " + "Caught IOException while closing ServerSocket of " + name + " connection:\n\t" + e);402}403}404}405406/**407* Close socket of connection to remote host.408*/409protected void closeHostConnection() {410if (socket != null) {411try {412socket.close();413logger.trace(TRACE_LEVEL_IO, "Socket closed: " + socket);414} catch (IOException e) {415logger.display("# WARNING: " + "Caught IOException while closing socket of " + name + " connection:\n\t" + e);416}417}418}419420/**421* Close socket streams.422*/423protected void closeSocketStreams() {424if (sout != null) {425try {426logger.trace(TRACE_LEVEL_IO, "Closing socket output stream: " + sout);427sout.close();428logger.trace(TRACE_LEVEL_IO, "Output stream closed: " + sout);429} catch (IOException e) {430logger.display("# WARNING: " + "Caught IOException while closing OutputStream of " + name + " connection:\n\t" + e);431}432}433if (sin != null) {434try {435logger.trace(TRACE_LEVEL_IO, "Closing socket input stream: " + sin);436sin.close();437logger.trace(TRACE_LEVEL_IO, "Input stream closed: " + sin);438} catch (IOException e) {439logger.display("# WARNING: " + "Caught IOException while closing InputStream of" + name + " connection:\n\t" + e);440}441}442}443444/**445* Close sockets and associated streams.446*/447protected void closeConnection() {448if (connectionClosed)449return;450451logger.trace(TRACE_LEVEL_IO, "Closing " + name + " connection");452closeSocketStreams();453closeHostConnection();454closeServerConnection();455connectionClosed = true;456}457458/**459* Make up socket streams after connection established.460*/461protected void makeSocketStreams() {462try {463logger.trace(TRACE_LEVEL_IO, "Getting input/output socket streams for " + name + " connection");464sout = socket.getOutputStream();465logger.trace(TRACE_LEVEL_IO, "Got socket output stream: " + sout);466sin = socket.getInputStream();467logger.trace(TRACE_LEVEL_IO, "Got socket input stream: " + sin);468} catch (IOException e) {469e.printStackTrace(logger.getOutStream());470throw new Failure("Caught exception while making streams for " + name + " connection:\n\t" + e);471}472}473474} // BasicSocketConnection475476/**477* This class implements connection channel via TCP/IP sockets. After connection478* established special inner threads are started, which periodically test the479* connection by pinging each other. If ping timeout occurs connection is closed480* and any thread waiting for read from this connection gets exception.481*482* @see #setPingTimeout(long)483*/484public class SocketConnection extends BasicSocketConnection {485486private static final long PING_INTERVAL = 1 * 1000; // milliseconds487488private static byte DATA_BYTE = (byte) 0x03;489490private static byte DISCONNECT_BYTE = (byte) 0x04;491492private final Object inLock = new Object();493private ObjectInputStream in = null;494495private final Object outLock = new Object();496private ObjectOutputStream out = null;497498private volatile long pingTimeout = 0; // don't use ping499500/**501* Make an empty connection with specified name.502*503* @param log504* Log object for printing log messages505* @param name506* connection name507*/508public SocketConnection(Log log, String name) {509this(new Log.Logger(log, name + " connection> "), name);510}511512/**513* Make an empty connection with specified name.514*515* @param logger516* Logger object for printing log messages517* @param name518* connection name519*/520public SocketConnection(Log.Logger logger, String name) {521super(logger, name);522}523524/**525* Set ping timeout in milliseconds (0 means don't use ping at all).526*/527public void setPingTimeout(long timeout) {528logger.display("# WARNING: Setting ping timeout for " + name + " connection ingnored: " + timeout + " ms");529pingTimeout = timeout;530}531532/**533* Returns value of current ping timeout in milliseconds (0 means ping is534* not used).535*/536public long getPingTimeout() {537return pingTimeout;538}539540/**541* Receive an object from remote host.542*/543public Object readObject() {544if (!isConnected()) {545throw new Failure("Unable to read object from not established " + name + " connection");546}547548try {549return doReadObject();550} catch (EOFException e) {551return null;552} catch (Exception e) {553e.printStackTrace(logger.getOutStream());554throw new Failure("Caught Exception while reading an object from " + name + " connection:\n\t" + e);555}556}557558/**559* Send an object to remote host.560*/561public void writeObject(Object object) {562if (!isConnected()) {563throw new Failure("Unable to send object throw not established " + name + " connection:\n\t" + object);564}565566try {567doWriteObject(object);568} catch (IOException e) {569e.printStackTrace(logger.getOutStream());570throw new Failure("Caught IOException while writing an object to " + name + " connection:\n\t" + e);571}572}573574/**575* Close socket and associated streams and finish all internal threads.576*/577public void close() {578if (!closed) {579// disconnect();580shouldStop = true;581super.close();582closed = true;583}584}585586/**587* Perform some actions after connection has established.588*/589protected void onConnected() {590super.onConnected();591}592593/**594* Do write an object to the connection channel.595*/596private void doWriteObject(Object object) throws IOException {597logger.trace(TRACE_LEVEL_IO, "writing object: " + object);598synchronized(outLock) {599out.writeObject(object);600out.flush();601}602logger.trace(TRACE_LEVEL_PACKETS, "* sent: " + object);603}604605/**606* Do read an object from the connection channel.607*/608private Object doReadObject() throws IOException, ClassNotFoundException {609logger.trace(TRACE_LEVEL_IO, "Reading object");610Object object = null;611synchronized(inLock) {612object = in.readObject();613}614logger.trace(TRACE_LEVEL_PACKETS, "* recv: " + object);615return object;616}617618/**619* Close socket streams.620*/621protected void closeSocketStreams() {622synchronized(outLock) {623if (out != null) {624try {625logger.trace(TRACE_LEVEL_IO, "Closing socket output stream: " + out);626out.close();627logger.trace(TRACE_LEVEL_IO, "Output stream closed: " + out);628} catch (IOException e) {629logger.display("# WARNING: " + "Caught IOException while closing ObjectOutputStream of " + name + " connection:\n\t" + e);630}631}632}633synchronized(inLock) {634if (in != null) {635try {636logger.trace(TRACE_LEVEL_IO, "Closing socket input stream: " + in);637in.close();638logger.trace(TRACE_LEVEL_IO, "Input stream closed: " + in);639} catch (IOException e) {640logger.display("# WARNING: " + "Caught IOException while closing ObjectInputStream of" + name + " connection:\n\t" + e);641}642}643}644super.closeSocketStreams();645}646647/**648* Close sockets and associated streams.649*/650protected void closeConnection() {651if (connectionClosed)652return;653connected = false;654shouldStop = true;655super.closeConnection();656}657658/**659* Make up object streams for socket.660*/661protected void makeSocketStreams() {662try {663logger.trace(TRACE_LEVEL_IO, "Making input/output object streams for " + name + " connection");664synchronized(outLock) {665out = new ObjectOutputStream(socket.getOutputStream());666out.flush();667}668logger.trace(TRACE_LEVEL_IO, "Output stream created: " + out);669synchronized(inLock) {670in = new ObjectInputStream(socket.getInputStream());671}672logger.trace(TRACE_LEVEL_IO, "Input stream created: " + in);673} catch (IOException e) {674e.printStackTrace(logger.getOutStream());675throw new Failure("Caught exception while making streams for " + name + " connection:\n\t" + e);676}677}678679} // SocketConnection680681682