Path: blob/master/test/hotspot/jtreg/vmTestbase/nsk/share/jpda/SocketIOPipe.java
66661 views
/*1* Copyright (c) 2007, 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*/22package nsk.share.jpda;2324import java.io.IOException;25import java.net.InetSocketAddress;26import java.net.ServerSocket;27import nsk.share.*;2829/*30* This class represents communication channel based on TCP/IP sockets.31* Usage of this class implies creation of objects of 2 types: server SocketIOPipe object32* (this object creates server socket and waits for incoming connection) and client33* SocketIOPipe (this object attaches to server).34*35* Server and client objects should be created using special static methods provided by this class,36* for example 'createServerIOPipe(Log log, int port, long timeout)' for server SocketIOPipe37* and 'createClientIOPipe(Log log, String host, int port, long timeout)' for client SocketIOPipe.38*39* When SocketIOPipe is created it can be used to send and receive strings using methods 'readln()' and 'println(String s)'.40* TCP/IP connection is established at the first attempt to read or write data.41*42* For example, if client process should send string 'OK' to the server process which is run43* at the host 'SERVER_HOST' following code can be written:44*45* Server side:46*47* // SocketIOPipe creates ServerSocket listening given port48* SocketIOPipe pipe = SocketIOPipe.createServerIOPipe(log, port, timeoutValue);49*50* // SocketIOPipe waits connection from client and reads data sent by the client51* String command = pipe.readln();52*53* Client side:54*55* // initialize SocketIOPipe with given values of server host name and port56* SocketIOPipe pipe = SocketIOPipe.createClientIOPipe(log, 'SERVER_HOST', port, timeoutValue);57*58* String command = "OK";59* // SocketIOPipe tries to create socket and send command to the server60* pipe.println(command);61*62*/63public class SocketIOPipe extends Log.Logger implements Finalizable {6465public static final int DEFAULT_TIMEOUT_VALUE = 1 * 60 * 1000;6667public static final String DEFAULT_PIPE_LOG_PREFIX = "SocketIOPipe> ";6869protected boolean listening;7071protected String host;7273protected int port;7475protected long timeout;7677protected SocketConnection connection;7879protected volatile boolean shouldStop;8081protected ServerSocket serverSocket;8283protected String name;8485/**86* Make general <code>IOPipe</code> object with specified parameters.87*/88protected SocketIOPipe(String name, Log log, String logPrefix, String host, int port, long timeout, boolean listening) {89super(log, logPrefix);90this.host = host;91this.port = port;92this.timeout = timeout;93this.listening = listening;94this.name = name;95}9697/**98* Make general <code>IOPipe</code> object with specified parameters.99*/100protected SocketIOPipe(Log log, String logPrefix, String host, int port, long timeout, boolean listening) {101super(log, logPrefix);102this.host = host;103this.port = port;104this.timeout = timeout;105this.listening = listening;106}107108/**109* Create listening SocketIOPipe using given port110*/111public static SocketIOPipe createServerIOPipe(Log log, int port, long timeout) {112SocketIOPipe pipe = new SocketIOPipe(log, DEFAULT_PIPE_LOG_PREFIX, null, 0, timeout, true);113114try {115ServerSocket ss = new ServerSocket();116if (port == 0) {117// Only need SO_REUSEADDR if we're using a fixed port. If we118// start seeing EADDRINUSE due to collisions in free ports119// then we should retry the bind() a few times.120ss.setReuseAddress(false);121}122ss.bind(new InetSocketAddress(port));123pipe.setServerSocket(ss);124} catch (IOException e) {125e.printStackTrace(log.getOutStream());126throw new Failure("Caught IOException while binding for IOPipe connection: \n\t" + e);127}128129return pipe;130}131132/**133* Create listening SocketIOPipe using any free port134*/135public static SocketIOPipe createServerIOPipe(Log log, long timeout) {136return createServerIOPipe(log, 0, timeout);137}138139/**140* Create attaching SocketIOPipe using given port and timeout141*/142public static SocketIOPipe createClientIOPipe(Log log, String host, int port, long timeout) {143return new SocketIOPipe(log, DEFAULT_PIPE_LOG_PREFIX, host, port, timeout, false);144}145146/**147* Return true if <code>IOPipe</code> connection established.148*/149public boolean isConnected() {150return (connection != null && connection.isConnected());151}152153/**154* Returns port number used by SocketIOPipe155*/156public int getPort() {157return port;158}159160protected void setServerSocket(ServerSocket serverSocket) {161this.serverSocket = serverSocket;162if (serverSocket != null)163port = serverSocket.getLocalPort();164}165166/**167* Write (and flush) given <code>line</code> to this168* <code>IOPipe</code> cnannel.169*170* @throws Failure if error occured while sending data171*/172public void println(String line) {173if (connection == null) {174connect();175}176connection.writeObject(line);177}178179/**180* Read a text line from this <code>IOPipe</code> channel,181* or return <i>null</i> if EOF reached.182*183* @throws Failure if error occured while reading data184*/185public String readln() {186if (connection == null) {187connect();188}189String line = (String) connection.readObject();190return line;191}192193/**194* Close this <code>IOPipe</code> connection.195*/196public void close() {197shouldStop = true;198if (connection != null) {199connection.close();200}201}202203protected class ListenerThread extends Thread {204private SocketConnection connection;205private RuntimeException error;206207ListenerThread() {208super("PipeIO Listener Thread");209setDaemon(true);210211connection = new SocketConnection(SocketIOPipe.this, getName());212213if (serverSocket == null) {214connection.bind(port, timeout);215} else {216connection.setServerSocket(serverSocket);217}218}219220@Override221public void run() {222synchronized (this) {223try {224connection.accept(timeout);225} catch (Throwable th) {226error = th instanceof RuntimeException227? (RuntimeException)th228: new RuntimeException(th);229}230notifyAll();231}232}233234public SocketConnection getConnection() {235synchronized (this) {236while (!connection.isConnected() && error == null) {237try {238wait();239} catch (InterruptedException e) {240}241}242if (error != null) {243throw error;244}245return connection;246}247}248}249250private ListenerThread listenerThread;251252protected void startListening() {253if (listenerThread != null) {254throw new TestBug("already listening");255}256listenerThread = new ListenerThread();257listenerThread.start();258}259260/**261* Establish <code>IOPipe</code> connection by attaching or accepting262* connection appropriately.263*/264protected void connect() {265if (connection != null) {266throw new TestBug("IOPipe connection is already established");267}268269if (shouldStop)270return;271272if (listening) {273// listenerThread == null means the test is not updated yet274// to start IOPipe listening before launching debuggee.275if (listenerThread == null) {276// start listening and accept connection on the current thread277listenerThread = new ListenerThread();278listenerThread.run();279}280connection = listenerThread.getConnection();281} else {282connection = new SocketConnection(this, getName());283// attach from the debuggee's side284connection.continueAttach(host, port, timeout);285}286}287288/**289* Set ping timeout in milliseconds (0 means don't use ping at all).290*/291public void setPingTimeout(long timeout) {292if (connection == null) {293throw new TestBug("Attempt to set ping timeout for not established connection");294}295connection.setPingTimeout(timeout);296}297298/**299* Returns value of current ping timeout in milliseconds (0 means ping is not used).300*/301public long getPingTimeout() {302if (connection == null) {303throw new TestBug("Attempt to get ping timeout for not established connection");304}305return connection.getPingTimeout();306}307308/**309* Perform finalization of the object by invoking close().310*/311protected void finalize() throws Throwable {312close();313super.finalize();314}315316/**317* Perform finalization of the object at exit by invoking finalize().318*/319public void finalizeAtExit() throws Throwable {320finalize();321}322323/**324* Field 'pipeCounter' and method 'getNextPipeNumber' are used to construct unique names for SocketIOPipes325*/326private static int pipeCounter;327328private synchronized int getNextPipeNumber() {329return pipeCounter++;330}331332/**333* Construct name for SocketIOPipe if it wasn't specified334*/335private String getName() {336if (name == null) {337name = "SocketIOPipe-" + getNextPipeNumber();338}339340return name;341}342}343344345