Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/jdk17u
Path: blob/master/test/hotspot/jtreg/vmTestbase/nsk/share/jpda/SocketConnection.java
66661 views
1
/*
2
* Copyright (c) 2001, 2021, 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
24
package nsk.share.jpda;
25
26
import java.io.*;
27
import java.net.*;
28
29
import nsk.share.*;
30
31
/**
32
* This class implements basic connection channel via TCP/IP sockets.
33
*/
34
class BasicSocketConnection {
35
36
protected static int TRACE_LEVEL_PACKETS = 10;
37
38
protected static int TRACE_LEVEL_THREADS = 20;
39
40
protected static int TRACE_LEVEL_ACTIONS = 30;
41
42
protected static int TRACE_LEVEL_SOCKETS = 40;
43
44
protected static int TRACE_LEVEL_IO = 50;
45
46
protected String name = null;
47
48
protected ServerSocket serverSocket = null;
49
50
protected Socket socket = null;
51
52
protected InputStream sin = null;
53
54
protected OutputStream sout = null;
55
56
protected volatile boolean connected = false;
57
58
protected volatile boolean closed = false;
59
60
protected volatile boolean connectionClosed = false;
61
62
protected volatile boolean shouldStop = false;
63
64
protected Log.Logger logger = null;
65
66
/**
67
* Make an empty connection with specified name.
68
*
69
* @param logger
70
* Logger object for printing log messages
71
* @param name
72
* connection name
73
*/
74
public BasicSocketConnection(Log.Logger logger, String name) {
75
this.logger = logger;
76
this.name = name;
77
}
78
79
/**
80
* Try to bind connection to the local port.
81
*
82
* @param port
83
* port number to bind to
84
*
85
* @throws IOException
86
* if error occured while binding
87
*/
88
protected void tryBind(int port) throws IOException {
89
logger.trace(TRACE_LEVEL_IO, "Binding for " + name + " connection to port: " + port);
90
serverSocket = new ServerSocket(port, 1);
91
logger.trace(TRACE_LEVEL_IO, "Bound for " + name + " connection to port: " + port);
92
}
93
94
/**
95
* Bind connection to the local port for specified timeout.
96
*
97
* @param port
98
* port number to bind to
99
* @param timeout
100
* binding timeout in milliseconds
101
*
102
* @throws Failure
103
* if error ocured while binding
104
*/
105
protected void bind(int port, long timeout) {
106
BindException bindException = null;
107
long timeToFinish = System.currentTimeMillis() + timeout;
108
for (long i = 0; !shouldStop && (timeout == 0 || System.currentTimeMillis() < timeToFinish); i++) {
109
try {
110
tryBind(port);
111
return;
112
} catch (BindException e) {
113
bindException = e;
114
logger.display("Attempt #" + i + " to bind to port " + port + " failed:\n\t" + e);
115
try {
116
Thread.sleep(DebugeeBinder.CONNECT_TRY_DELAY);
117
} catch (InterruptedException ie) {
118
ie.printStackTrace(logger.getOutStream());
119
throw new Failure("Thread interrupted while binding for " + name + " connection to port " + port + ":\n\t" + ie);
120
}
121
} catch (IOException e) {
122
e.printStackTrace(logger.getOutStream());
123
throw new Failure("Caught IOException while binding for " + name + " connection to port " + port + ":\n\t" + e);
124
}
125
}
126
throw new Failure("Unable to bind for " + name + " connection to port " + port + " for " + timeout + "ms timeout:\n\t" + bindException);
127
}
128
129
/**
130
* Accept connection at the bound port for specified timeout.
131
*
132
* @param timeout
133
* accepting timeout in milliseconds
134
*
135
* @throws Failure
136
* if error occured while accepting connection
137
*/
138
public void accept(long timeout) {
139
int port = serverSocket.getLocalPort();
140
logger.trace(TRACE_LEVEL_IO, "Listening for " + name + " connection at port: " + port);
141
socket = null;
142
try {
143
if (timeout > Integer.MAX_VALUE) {
144
throw new TestBug("Too large timeout long value: " + timeout + " (can't cast it to int)");
145
}
146
147
serverSocket.setSoTimeout((int)timeout);
148
149
long waitStartTime = System.currentTimeMillis();
150
151
/*
152
* We found that sometimes (very rarely) on Solaris ServerSocket.accept() throws InterruptedIOException
153
* even if connection timeout (specified through ServerSocket.setSoTimeout) didn't expire.
154
* Following code tries to catch such case and call ServerSocket.accept() while timeout didn't expire.
155
*/
156
do {
157
try {
158
socket = serverSocket.accept();
159
logger.trace(TRACE_LEVEL_IO, "Accepted " + name + " connection at port: " + port);
160
} catch (InterruptedIOException e) {
161
long interruptTime = System.currentTimeMillis();
162
long waitTime = interruptTime - waitStartTime;
163
164
logger.display("Caught InterruptedIOException. Wait start time: " + waitStartTime + ", exception was thrown at: "
165
+ interruptTime + ", wait time: " + (interruptTime - waitStartTime) + ", actual timeout: " + timeout);
166
167
// if waitTime was too small call ServerSocket.accept() one more time
168
if (!shouldStop && (waitTime < (timeout / 2))) {
169
logger.display("InterruptedIOException was thrown too early, trying to call ServerSocket.accept() one more time");
170
continue;
171
} else {
172
if (!shouldStop) {
173
logger.complain("Caught InterruptedIOException while listening for " + name + " connection at port " + port + ":\n\t" + e);
174
throw new Failure("Connection for " + name +
175
" at port " + port +
176
" wasn't accepted in " + timeout + "ms");
177
} else {
178
logger.display("Listening was interrupted (caught InterruptedIOException while listening for " + name + " connection at port " + port + ":\n\t" + e + ")");
179
break;
180
}
181
}
182
}
183
} while (socket == null);
184
185
} catch (IOException e) {
186
if (!shouldStop) {
187
e.printStackTrace(logger.getOutStream());
188
throw new Failure("Caught IOException while listening for " + name + " connection at port " + port + ":\n\t" + e);
189
} else {
190
logger.display("Listening was interrupted (caught InterruptedIOException while listening for " + name + " connection at port " + port + ":\n\t" + e + ")");
191
}
192
} finally {
193
closeServerConnection();
194
}
195
196
if (!shouldStop) {
197
if (socket == null) {
198
throw new Failure("No " + name + " connection accepted at port " + port + " for " + timeout + "ms timeout");
199
}
200
201
onConnected();
202
}
203
}
204
205
/**
206
* Attach connection to the remote host and port.
207
*
208
* @param host
209
* name of remote host to attach to
210
* @param port
211
* port number to attach to
212
*
213
* @throws Failure
214
* if error occured while attaching
215
*/
216
public void attach(String host, int port) {
217
try {
218
logger.trace(TRACE_LEVEL_IO, "Attaching for " + name + " connection to host: " + host + ":" + port);
219
socket = new Socket(host, port);
220
socket.setTcpNoDelay(true);
221
logger.trace(TRACE_LEVEL_IO, "Attached for " + name + " connection to host: " + host + ":" + port);
222
} catch (IOException e) {
223
e.printStackTrace(logger.getOutStream());
224
throw new Failure("Caught IOException while attaching for " + name + " connection to " + host + ":" + port + ":\n\t" + e);
225
}
226
if (!shouldStop) {
227
onConnected();
228
}
229
}
230
231
/**
232
* Continuously attach to the remote host for the specified timeout.
233
*
234
* @param host
235
* name of remote host to attach to
236
* @param port
237
* port number to attach to
238
* @param timeout
239
* attaching timeout in milliseconds
240
*
241
* @throws Failure
242
* if error occured while attaching
243
*/
244
public void continueAttach(String host, int port, long timeout) {
245
socket = null;
246
long timeToFinish = System.currentTimeMillis() + timeout;
247
ConnectException lastException = null;
248
logger.trace(TRACE_LEVEL_IO, "Attaching for " + name + " connection to host: " + host + ":" + port);
249
try {
250
for (long i = 0; !shouldStop && (timeout == 0 || System.currentTimeMillis() < timeToFinish); i++) {
251
try {
252
socket = new Socket(host, port);
253
logger.trace(TRACE_LEVEL_IO, "Attached for " + name + " connection to host: " + host + ":" + port);
254
break;
255
} catch (ConnectException e) {
256
logger.display("Attempt #" + i + " to attach for " + name + " connection failed:\n\t" + e);
257
lastException = e;
258
// sleep between attempts
259
try {
260
Thread.sleep(DebugeeBinder.CONNECT_TRY_DELAY);
261
} catch (InterruptedException ie) {
262
throw new Failure("Thread interrupted while attaching for " + name + " connection to " + host + ":" + port + ":\n\t" + ie);
263
}
264
}
265
}
266
267
} catch (IOException e) {
268
e.printStackTrace(logger.getOutStream());
269
throw new Failure("Caught IOException while attaching for " + name + " connection to " + host + ":" + port + ":\n\t" + e);
270
}
271
272
if (!shouldStop) {
273
if (socket == null) {
274
throw new Failure("Unable to attach for " + name + " connection to " + host + ":" + port + " for " + timeout + "ms timeout:\n\t"
275
+ lastException);
276
}
277
278
onConnected();
279
}
280
}
281
282
/**
283
* Set already bound serverSocket for further connection.
284
*/
285
public void setServerSocket(ServerSocket serverSocket) {
286
this.serverSocket = serverSocket;
287
}
288
289
/**
290
* Set already connected socket for connection.
291
*/
292
public void setSocket(Socket socket) {
293
this.socket = socket;
294
if (!shouldStop) {
295
onConnected();
296
}
297
}
298
299
/**
300
* Get socket of already established connection.
301
*/
302
public Socket getSocket() {
303
return socket;
304
}
305
306
/**
307
* Check if connection is established.
308
*/
309
public boolean isConnected() {
310
return connected;
311
}
312
313
/**
314
* Close socket and associated streams.
315
*/
316
public void close() {
317
if (!closed) {
318
shouldStop = true;
319
closeConnection();
320
closed = true;
321
}
322
}
323
324
/**
325
* Send the specified byte throw the connection.
326
*/
327
public void writeByte(byte b) throws IOException {
328
logger.trace(TRACE_LEVEL_IO, "Writing byte: " + b);
329
sout.write(b);
330
sout.flush();
331
logger.trace(TRACE_LEVEL_IO, "Wrote byte: " + b);
332
}
333
334
/**
335
* Read a byte and return it or -1.
336
*/
337
public int readByte() throws IOException {
338
logger.trace(TRACE_LEVEL_IO, "Reading byte");
339
int b = sin.read();
340
logger.trace(TRACE_LEVEL_IO, "Received byte: " + b);
341
return b;
342
}
343
344
/**
345
* Perform some actions after connection established.
346
*/
347
protected void onConnected() {
348
if (!shouldStop) {
349
setSocketOptions();
350
makeSocketStreams();
351
connected = true;
352
}
353
}
354
355
/**
356
* Set socket options after connection established.
357
*/
358
protected void setSocketOptions() {
359
}
360
361
/**
362
* Close server socket.
363
*/
364
protected void closeServerConnection() {
365
if (serverSocket != null) {
366
try {
367
serverSocket.close();
368
logger.trace(TRACE_LEVEL_IO, "ServerSocket closed: " + serverSocket);
369
} catch (IOException e) {
370
logger.display("# WARNING: " + "Caught IOException while closing ServerSocket of " + name + " connection:\n\t" + e);
371
}
372
}
373
}
374
375
/**
376
* Close socket of connection to remote host.
377
*/
378
protected void closeHostConnection() {
379
if (socket != null) {
380
try {
381
socket.close();
382
logger.trace(TRACE_LEVEL_IO, "Socket closed: " + socket);
383
} catch (IOException e) {
384
logger.display("# WARNING: " + "Caught IOException while closing socket of " + name + " connection:\n\t" + e);
385
}
386
}
387
}
388
389
/**
390
* Close socket streams.
391
*/
392
protected void closeSocketStreams() {
393
if (sout != null) {
394
try {
395
logger.trace(TRACE_LEVEL_IO, "Closing socket output stream: " + sout);
396
sout.close();
397
logger.trace(TRACE_LEVEL_IO, "Output stream closed: " + sout);
398
} catch (IOException e) {
399
logger.display("# WARNING: " + "Caught IOException while closing OutputStream of " + name + " connection:\n\t" + e);
400
}
401
}
402
if (sin != null) {
403
try {
404
logger.trace(TRACE_LEVEL_IO, "Closing socket input stream: " + sin);
405
sin.close();
406
logger.trace(TRACE_LEVEL_IO, "Input stream closed: " + sin);
407
} catch (IOException e) {
408
logger.display("# WARNING: " + "Caught IOException while closing InputStream of" + name + " connection:\n\t" + e);
409
}
410
}
411
}
412
413
/**
414
* Close sockets and associated streams.
415
*/
416
protected void closeConnection() {
417
if (connectionClosed)
418
return;
419
420
logger.trace(TRACE_LEVEL_IO, "Closing " + name + " connection");
421
closeSocketStreams();
422
closeHostConnection();
423
closeServerConnection();
424
connectionClosed = true;
425
}
426
427
/**
428
* Make up socket streams after connection established.
429
*/
430
protected void makeSocketStreams() {
431
try {
432
logger.trace(TRACE_LEVEL_IO, "Getting input/output socket streams for " + name + " connection");
433
sout = socket.getOutputStream();
434
logger.trace(TRACE_LEVEL_IO, "Got socket output stream: " + sout);
435
sin = socket.getInputStream();
436
logger.trace(TRACE_LEVEL_IO, "Got socket input stream: " + sin);
437
} catch (IOException e) {
438
e.printStackTrace(logger.getOutStream());
439
throw new Failure("Caught exception while making streams for " + name + " connection:\n\t" + e);
440
}
441
}
442
443
} // BasicSocketConnection
444
445
/**
446
* This class implements connection channel via TCP/IP sockets. After connection
447
* established special inner threads are started, which periodically test the
448
* connection by pinging each other. If ping timeout occurs connection is closed
449
* and any thread waiting for read from this connection gets exception.
450
*
451
* @see #setPingTimeout(long)
452
*/
453
public class SocketConnection extends BasicSocketConnection {
454
455
private static final long PING_INTERVAL = 1 * 1000; // milliseconds
456
457
private static byte DATA_BYTE = (byte) 0x03;
458
459
private static byte DISCONNECT_BYTE = (byte) 0x04;
460
461
private final Object inLock = new Object();
462
private ObjectInputStream in = null;
463
464
private final Object outLock = new Object();
465
private ObjectOutputStream out = null;
466
467
private volatile long pingTimeout = 0; // don't use ping
468
469
/**
470
* Make an empty connection with specified name.
471
*
472
* @param log
473
* Log object for printing log messages
474
* @param name
475
* connection name
476
*/
477
public SocketConnection(Log log, String name) {
478
this(new Log.Logger(log, name + " connection> "), name);
479
}
480
481
/**
482
* Make an empty connection with specified name.
483
*
484
* @param logger
485
* Logger object for printing log messages
486
* @param name
487
* connection name
488
*/
489
public SocketConnection(Log.Logger logger, String name) {
490
super(logger, name);
491
}
492
493
/**
494
* Set ping timeout in milliseconds (0 means don't use ping at all).
495
*/
496
public void setPingTimeout(long timeout) {
497
logger.display("# WARNING: Setting ping timeout for " + name + " connection ingnored: " + timeout + " ms");
498
pingTimeout = timeout;
499
}
500
501
/**
502
* Returns value of current ping timeout in milliseconds (0 means ping is
503
* not used).
504
*/
505
public long getPingTimeout() {
506
return pingTimeout;
507
}
508
509
/**
510
* Receive an object from remote host.
511
*/
512
public Object readObject() {
513
if (!isConnected()) {
514
throw new Failure("Unable to read object from not established " + name + " connection");
515
}
516
517
try {
518
return doReadObject();
519
} catch (EOFException e) {
520
return null;
521
} catch (Exception e) {
522
e.printStackTrace(logger.getOutStream());
523
throw new Failure("Caught Exception while reading an object from " + name + " connection:\n\t" + e);
524
}
525
}
526
527
/**
528
* Send an object to remote host.
529
*/
530
public void writeObject(Object object) {
531
if (!isConnected()) {
532
throw new Failure("Unable to send object throw not established " + name + " connection:\n\t" + object);
533
}
534
535
try {
536
doWriteObject(object);
537
} catch (IOException e) {
538
e.printStackTrace(logger.getOutStream());
539
throw new Failure("Caught IOException while writing an object to " + name + " connection:\n\t" + e);
540
}
541
}
542
543
/**
544
* Close socket and associated streams and finish all internal threads.
545
*/
546
public void close() {
547
if (!closed) {
548
// disconnect();
549
shouldStop = true;
550
super.close();
551
closed = true;
552
}
553
}
554
555
/**
556
* Perform some actions after connection has established.
557
*/
558
protected void onConnected() {
559
super.onConnected();
560
}
561
562
/**
563
* Do write an object to the connection channel.
564
*/
565
private void doWriteObject(Object object) throws IOException {
566
logger.trace(TRACE_LEVEL_IO, "writing object: " + object);
567
synchronized(outLock) {
568
out.writeObject(object);
569
out.flush();
570
}
571
logger.trace(TRACE_LEVEL_PACKETS, "* sent: " + object);
572
}
573
574
/**
575
* Do read an object from the connection channel.
576
*/
577
private Object doReadObject() throws IOException, ClassNotFoundException {
578
logger.trace(TRACE_LEVEL_IO, "Reading object");
579
Object object = null;
580
synchronized(inLock) {
581
object = in.readObject();
582
}
583
logger.trace(TRACE_LEVEL_PACKETS, "* recv: " + object);
584
return object;
585
}
586
587
/**
588
* Close socket streams.
589
*/
590
protected void closeSocketStreams() {
591
synchronized(outLock) {
592
if (out != null) {
593
try {
594
logger.trace(TRACE_LEVEL_IO, "Closing socket output stream: " + out);
595
out.close();
596
logger.trace(TRACE_LEVEL_IO, "Output stream closed: " + out);
597
} catch (IOException e) {
598
logger.display("# WARNING: " + "Caught IOException while closing ObjectOutputStream of " + name + " connection:\n\t" + e);
599
}
600
}
601
}
602
synchronized(inLock) {
603
if (in != null) {
604
try {
605
logger.trace(TRACE_LEVEL_IO, "Closing socket input stream: " + in);
606
in.close();
607
logger.trace(TRACE_LEVEL_IO, "Input stream closed: " + in);
608
} catch (IOException e) {
609
logger.display("# WARNING: " + "Caught IOException while closing ObjectInputStream of" + name + " connection:\n\t" + e);
610
}
611
}
612
}
613
super.closeSocketStreams();
614
}
615
616
/**
617
* Close sockets and associated streams.
618
*/
619
protected void closeConnection() {
620
if (connectionClosed)
621
return;
622
connected = false;
623
shouldStop = true;
624
super.closeConnection();
625
}
626
627
/**
628
* Make up object streams for socket.
629
*/
630
protected void makeSocketStreams() {
631
try {
632
logger.trace(TRACE_LEVEL_IO, "Making input/output object streams for " + name + " connection");
633
synchronized(outLock) {
634
out = new ObjectOutputStream(socket.getOutputStream());
635
out.flush();
636
}
637
logger.trace(TRACE_LEVEL_IO, "Output stream created: " + out);
638
synchronized(inLock) {
639
in = new ObjectInputStream(socket.getInputStream());
640
}
641
logger.trace(TRACE_LEVEL_IO, "Input stream created: " + in);
642
} catch (IOException e) {
643
e.printStackTrace(logger.getOutStream());
644
throw new Failure("Caught exception while making streams for " + name + " connection:\n\t" + e);
645
}
646
}
647
648
} // SocketConnection
649
650