Path: blob/master/test/hotspot/jtreg/vmTestbase/nsk/share/jpda/SocketConnection.java
66661 views
/*1* Copyright (c) 2001, 2021, 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 volatile boolean connected = false;5657protected volatile boolean closed = false;5859protected volatile boolean connectionClosed = false;6061protected volatile boolean shouldStop = false;6263protected Log.Logger logger = null;6465/**66* Make an empty connection with specified name.67*68* @param logger69* Logger object for printing log messages70* @param name71* connection name72*/73public BasicSocketConnection(Log.Logger logger, String name) {74this.logger = logger;75this.name = name;76}7778/**79* Try to bind connection to the local port.80*81* @param port82* port number to bind to83*84* @throws IOException85* if error occured while binding86*/87protected void tryBind(int port) throws IOException {88logger.trace(TRACE_LEVEL_IO, "Binding for " + name + " connection to port: " + port);89serverSocket = new ServerSocket(port, 1);90logger.trace(TRACE_LEVEL_IO, "Bound for " + name + " connection to port: " + port);91}9293/**94* Bind connection to the local port for specified timeout.95*96* @param port97* port number to bind to98* @param timeout99* binding timeout in milliseconds100*101* @throws Failure102* if error ocured while binding103*/104protected void bind(int port, long timeout) {105BindException bindException = null;106long timeToFinish = System.currentTimeMillis() + timeout;107for (long i = 0; !shouldStop && (timeout == 0 || System.currentTimeMillis() < timeToFinish); i++) {108try {109tryBind(port);110return;111} catch (BindException e) {112bindException = e;113logger.display("Attempt #" + i + " to bind to port " + port + " failed:\n\t" + e);114try {115Thread.sleep(DebugeeBinder.CONNECT_TRY_DELAY);116} catch (InterruptedException ie) {117ie.printStackTrace(logger.getOutStream());118throw new Failure("Thread interrupted while binding for " + name + " connection to port " + port + ":\n\t" + ie);119}120} catch (IOException e) {121e.printStackTrace(logger.getOutStream());122throw new Failure("Caught IOException while binding for " + name + " connection to port " + port + ":\n\t" + e);123}124}125throw new Failure("Unable to bind for " + name + " connection to port " + port + " for " + timeout + "ms timeout:\n\t" + bindException);126}127128/**129* Accept connection at the bound port for specified timeout.130*131* @param timeout132* accepting timeout in milliseconds133*134* @throws Failure135* if error occured while accepting connection136*/137public void accept(long timeout) {138int port = serverSocket.getLocalPort();139logger.trace(TRACE_LEVEL_IO, "Listening for " + name + " connection at port: " + port);140socket = null;141try {142if (timeout > Integer.MAX_VALUE) {143throw new TestBug("Too large timeout long value: " + timeout + " (can't cast it to int)");144}145146serverSocket.setSoTimeout((int)timeout);147148long waitStartTime = System.currentTimeMillis();149150/*151* We found that sometimes (very rarely) on Solaris ServerSocket.accept() throws InterruptedIOException152* even if connection timeout (specified through ServerSocket.setSoTimeout) didn't expire.153* Following code tries to catch such case and call ServerSocket.accept() while timeout didn't expire.154*/155do {156try {157socket = serverSocket.accept();158logger.trace(TRACE_LEVEL_IO, "Accepted " + name + " connection at port: " + port);159} catch (InterruptedIOException e) {160long interruptTime = System.currentTimeMillis();161long waitTime = interruptTime - waitStartTime;162163logger.display("Caught InterruptedIOException. Wait start time: " + waitStartTime + ", exception was thrown at: "164+ interruptTime + ", wait time: " + (interruptTime - waitStartTime) + ", actual timeout: " + timeout);165166// if waitTime was too small call ServerSocket.accept() one more time167if (!shouldStop && (waitTime < (timeout / 2))) {168logger.display("InterruptedIOException was thrown too early, trying to call ServerSocket.accept() one more time");169continue;170} else {171if (!shouldStop) {172logger.complain("Caught InterruptedIOException while listening for " + name + " connection at port " + port + ":\n\t" + e);173throw new Failure("Connection for " + name +174" at port " + port +175" wasn't accepted in " + timeout + "ms");176} else {177logger.display("Listening was interrupted (caught InterruptedIOException while listening for " + name + " connection at port " + port + ":\n\t" + e + ")");178break;179}180}181}182} while (socket == null);183184} catch (IOException e) {185if (!shouldStop) {186e.printStackTrace(logger.getOutStream());187throw new Failure("Caught IOException while listening for " + name + " connection at port " + port + ":\n\t" + e);188} else {189logger.display("Listening was interrupted (caught InterruptedIOException while listening for " + name + " connection at port " + port + ":\n\t" + e + ")");190}191} finally {192closeServerConnection();193}194195if (!shouldStop) {196if (socket == null) {197throw new Failure("No " + name + " connection accepted at port " + port + " for " + timeout + "ms timeout");198}199200onConnected();201}202}203204/**205* Attach connection to the remote host and port.206*207* @param host208* name of remote host to attach to209* @param port210* port number to attach to211*212* @throws Failure213* if error occured while attaching214*/215public void attach(String host, int port) {216try {217logger.trace(TRACE_LEVEL_IO, "Attaching for " + name + " connection to host: " + host + ":" + port);218socket = new Socket(host, port);219socket.setTcpNoDelay(true);220logger.trace(TRACE_LEVEL_IO, "Attached for " + name + " connection to host: " + host + ":" + port);221} catch (IOException e) {222e.printStackTrace(logger.getOutStream());223throw new Failure("Caught IOException while attaching for " + name + " connection to " + host + ":" + port + ":\n\t" + e);224}225if (!shouldStop) {226onConnected();227}228}229230/**231* Continuously attach to the remote host for the specified timeout.232*233* @param host234* name of remote host to attach to235* @param port236* port number to attach to237* @param timeout238* attaching timeout in milliseconds239*240* @throws Failure241* if error occured while attaching242*/243public void continueAttach(String host, int port, long timeout) {244socket = null;245long timeToFinish = System.currentTimeMillis() + timeout;246ConnectException lastException = null;247logger.trace(TRACE_LEVEL_IO, "Attaching for " + name + " connection to host: " + host + ":" + port);248try {249for (long i = 0; !shouldStop && (timeout == 0 || System.currentTimeMillis() < timeToFinish); i++) {250try {251socket = new Socket(host, port);252logger.trace(TRACE_LEVEL_IO, "Attached for " + name + " connection to host: " + host + ":" + port);253break;254} catch (ConnectException e) {255logger.display("Attempt #" + i + " to attach for " + name + " connection failed:\n\t" + e);256lastException = e;257// sleep between attempts258try {259Thread.sleep(DebugeeBinder.CONNECT_TRY_DELAY);260} catch (InterruptedException ie) {261throw new Failure("Thread interrupted while attaching for " + name + " connection to " + host + ":" + port + ":\n\t" + ie);262}263}264}265266} catch (IOException e) {267e.printStackTrace(logger.getOutStream());268throw new Failure("Caught IOException while attaching for " + name + " connection to " + host + ":" + port + ":\n\t" + e);269}270271if (!shouldStop) {272if (socket == null) {273throw new Failure("Unable to attach for " + name + " connection to " + host + ":" + port + " for " + timeout + "ms timeout:\n\t"274+ lastException);275}276277onConnected();278}279}280281/**282* Set already bound serverSocket for further connection.283*/284public void setServerSocket(ServerSocket serverSocket) {285this.serverSocket = serverSocket;286}287288/**289* Set already connected socket for connection.290*/291public void setSocket(Socket socket) {292this.socket = socket;293if (!shouldStop) {294onConnected();295}296}297298/**299* Get socket of already established connection.300*/301public Socket getSocket() {302return socket;303}304305/**306* Check if connection is established.307*/308public boolean isConnected() {309return connected;310}311312/**313* Close socket and associated streams.314*/315public void close() {316if (!closed) {317shouldStop = true;318closeConnection();319closed = true;320}321}322323/**324* Send the specified byte throw the connection.325*/326public void writeByte(byte b) throws IOException {327logger.trace(TRACE_LEVEL_IO, "Writing byte: " + b);328sout.write(b);329sout.flush();330logger.trace(TRACE_LEVEL_IO, "Wrote byte: " + b);331}332333/**334* Read a byte and return it or -1.335*/336public int readByte() throws IOException {337logger.trace(TRACE_LEVEL_IO, "Reading byte");338int b = sin.read();339logger.trace(TRACE_LEVEL_IO, "Received byte: " + b);340return b;341}342343/**344* Perform some actions after connection established.345*/346protected void onConnected() {347if (!shouldStop) {348setSocketOptions();349makeSocketStreams();350connected = true;351}352}353354/**355* Set socket options after connection established.356*/357protected void setSocketOptions() {358}359360/**361* Close server socket.362*/363protected void closeServerConnection() {364if (serverSocket != null) {365try {366serverSocket.close();367logger.trace(TRACE_LEVEL_IO, "ServerSocket closed: " + serverSocket);368} catch (IOException e) {369logger.display("# WARNING: " + "Caught IOException while closing ServerSocket of " + name + " connection:\n\t" + e);370}371}372}373374/**375* Close socket of connection to remote host.376*/377protected void closeHostConnection() {378if (socket != null) {379try {380socket.close();381logger.trace(TRACE_LEVEL_IO, "Socket closed: " + socket);382} catch (IOException e) {383logger.display("# WARNING: " + "Caught IOException while closing socket of " + name + " connection:\n\t" + e);384}385}386}387388/**389* Close socket streams.390*/391protected void closeSocketStreams() {392if (sout != null) {393try {394logger.trace(TRACE_LEVEL_IO, "Closing socket output stream: " + sout);395sout.close();396logger.trace(TRACE_LEVEL_IO, "Output stream closed: " + sout);397} catch (IOException e) {398logger.display("# WARNING: " + "Caught IOException while closing OutputStream of " + name + " connection:\n\t" + e);399}400}401if (sin != null) {402try {403logger.trace(TRACE_LEVEL_IO, "Closing socket input stream: " + sin);404sin.close();405logger.trace(TRACE_LEVEL_IO, "Input stream closed: " + sin);406} catch (IOException e) {407logger.display("# WARNING: " + "Caught IOException while closing InputStream of" + name + " connection:\n\t" + e);408}409}410}411412/**413* Close sockets and associated streams.414*/415protected void closeConnection() {416if (connectionClosed)417return;418419logger.trace(TRACE_LEVEL_IO, "Closing " + name + " connection");420closeSocketStreams();421closeHostConnection();422closeServerConnection();423connectionClosed = true;424}425426/**427* Make up socket streams after connection established.428*/429protected void makeSocketStreams() {430try {431logger.trace(TRACE_LEVEL_IO, "Getting input/output socket streams for " + name + " connection");432sout = socket.getOutputStream();433logger.trace(TRACE_LEVEL_IO, "Got socket output stream: " + sout);434sin = socket.getInputStream();435logger.trace(TRACE_LEVEL_IO, "Got socket input stream: " + sin);436} catch (IOException e) {437e.printStackTrace(logger.getOutStream());438throw new Failure("Caught exception while making streams for " + name + " connection:\n\t" + e);439}440}441442} // BasicSocketConnection443444/**445* This class implements connection channel via TCP/IP sockets. After connection446* established special inner threads are started, which periodically test the447* connection by pinging each other. If ping timeout occurs connection is closed448* and any thread waiting for read from this connection gets exception.449*450* @see #setPingTimeout(long)451*/452public class SocketConnection extends BasicSocketConnection {453454private static final long PING_INTERVAL = 1 * 1000; // milliseconds455456private static byte DATA_BYTE = (byte) 0x03;457458private static byte DISCONNECT_BYTE = (byte) 0x04;459460private final Object inLock = new Object();461private ObjectInputStream in = null;462463private final Object outLock = new Object();464private ObjectOutputStream out = null;465466private volatile long pingTimeout = 0; // don't use ping467468/**469* Make an empty connection with specified name.470*471* @param log472* Log object for printing log messages473* @param name474* connection name475*/476public SocketConnection(Log log, String name) {477this(new Log.Logger(log, name + " connection> "), name);478}479480/**481* Make an empty connection with specified name.482*483* @param logger484* Logger object for printing log messages485* @param name486* connection name487*/488public SocketConnection(Log.Logger logger, String name) {489super(logger, name);490}491492/**493* Set ping timeout in milliseconds (0 means don't use ping at all).494*/495public void setPingTimeout(long timeout) {496logger.display("# WARNING: Setting ping timeout for " + name + " connection ingnored: " + timeout + " ms");497pingTimeout = timeout;498}499500/**501* Returns value of current ping timeout in milliseconds (0 means ping is502* not used).503*/504public long getPingTimeout() {505return pingTimeout;506}507508/**509* Receive an object from remote host.510*/511public Object readObject() {512if (!isConnected()) {513throw new Failure("Unable to read object from not established " + name + " connection");514}515516try {517return doReadObject();518} catch (EOFException e) {519return null;520} catch (Exception e) {521e.printStackTrace(logger.getOutStream());522throw new Failure("Caught Exception while reading an object from " + name + " connection:\n\t" + e);523}524}525526/**527* Send an object to remote host.528*/529public void writeObject(Object object) {530if (!isConnected()) {531throw new Failure("Unable to send object throw not established " + name + " connection:\n\t" + object);532}533534try {535doWriteObject(object);536} catch (IOException e) {537e.printStackTrace(logger.getOutStream());538throw new Failure("Caught IOException while writing an object to " + name + " connection:\n\t" + e);539}540}541542/**543* Close socket and associated streams and finish all internal threads.544*/545public void close() {546if (!closed) {547// disconnect();548shouldStop = true;549super.close();550closed = true;551}552}553554/**555* Perform some actions after connection has established.556*/557protected void onConnected() {558super.onConnected();559}560561/**562* Do write an object to the connection channel.563*/564private void doWriteObject(Object object) throws IOException {565logger.trace(TRACE_LEVEL_IO, "writing object: " + object);566synchronized(outLock) {567out.writeObject(object);568out.flush();569}570logger.trace(TRACE_LEVEL_PACKETS, "* sent: " + object);571}572573/**574* Do read an object from the connection channel.575*/576private Object doReadObject() throws IOException, ClassNotFoundException {577logger.trace(TRACE_LEVEL_IO, "Reading object");578Object object = null;579synchronized(inLock) {580object = in.readObject();581}582logger.trace(TRACE_LEVEL_PACKETS, "* recv: " + object);583return object;584}585586/**587* Close socket streams.588*/589protected void closeSocketStreams() {590synchronized(outLock) {591if (out != null) {592try {593logger.trace(TRACE_LEVEL_IO, "Closing socket output stream: " + out);594out.close();595logger.trace(TRACE_LEVEL_IO, "Output stream closed: " + out);596} catch (IOException e) {597logger.display("# WARNING: " + "Caught IOException while closing ObjectOutputStream of " + name + " connection:\n\t" + e);598}599}600}601synchronized(inLock) {602if (in != null) {603try {604logger.trace(TRACE_LEVEL_IO, "Closing socket input stream: " + in);605in.close();606logger.trace(TRACE_LEVEL_IO, "Input stream closed: " + in);607} catch (IOException e) {608logger.display("# WARNING: " + "Caught IOException while closing ObjectInputStream of" + name + " connection:\n\t" + e);609}610}611}612super.closeSocketStreams();613}614615/**616* Close sockets and associated streams.617*/618protected void closeConnection() {619if (connectionClosed)620return;621connected = false;622shouldStop = true;623super.closeConnection();624}625626/**627* Make up object streams for socket.628*/629protected void makeSocketStreams() {630try {631logger.trace(TRACE_LEVEL_IO, "Making input/output object streams for " + name + " connection");632synchronized(outLock) {633out = new ObjectOutputStream(socket.getOutputStream());634out.flush();635}636logger.trace(TRACE_LEVEL_IO, "Output stream created: " + out);637synchronized(inLock) {638in = new ObjectInputStream(socket.getInputStream());639}640logger.trace(TRACE_LEVEL_IO, "Input stream created: " + in);641} catch (IOException e) {642e.printStackTrace(logger.getOutStream());643throw new Failure("Caught exception while making streams for " + name + " connection:\n\t" + e);644}645}646647} // SocketConnection648649650