Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/jdk/java/net/HugeDataTransferTest.java
41145 views
1
/*
2
* Copyright (c) 2017, 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
/**
25
* @test
26
* @bug 8185072
27
* @summary network006 times out in many configs in JDK10-hs nightly
28
* @run main/othervm/manual HugeDataTransferTest 1
29
*/
30
import java.io.FileNotFoundException;
31
import java.io.IOException;
32
import java.io.InputStream;
33
import java.io.OutputStream;
34
import java.io.PrintStream;
35
import java.net.InetAddress;
36
import java.net.ServerSocket;
37
import java.net.Socket;
38
import java.net.UnknownHostException;
39
import java.nio.file.Path;
40
import java.nio.file.Paths;
41
import java.util.Random;
42
43
/**
44
* This test makes huge number of data transfers between 2 Java virtual machines
45
* using the TCP/IP protocol, and checks if those data are transfered correctly.
46
* Both client and server VMs run on the same local computer and attach TCP/IP
47
* sockets to the local host, or to the loopback domain
48
* ``<code>localhost</code>'' (having IP address <code>127.0.0.1</code>).
49
*
50
* <p>
51
* In this test, 128 client/server connections are established. Once a
52
* connection is established, client passes a data parcel to server, and server
53
* reads that parcel and checks if it is same as expected (byte-to-byte equality
54
* is desired). Then server passes (some other) parcel to the client, and client
55
* reads and verifies those bytes. This ping-pong game is repeated 128 times;
56
* and after that each pair of sockets checks if there are no extra bytes
57
* accedentally passed through their connection.
58
*
59
* <p>
60
* Parcels lengths and contents are chosen randomly, and average parcel length
61
* is 128 bytes. So totally, each pair of sockets passes ~16Kb of data to each
62
* other, and thus ~32Kb of data are transfered by each sockets pair. Totally,
63
* ~4Mb of data are transfered by all client/server pairs.
64
*
65
* @author vtewari
66
*/
67
public class HugeDataTransferTest {
68
69
/**
70
* Timeout for TCP/IP sockets (currently set to 1 min).
71
*/
72
private static int SO_TIMEOUT;// = 2*60*1000;
73
74
/**
75
* Maximal number of connections this test should open simultaneously.
76
*/
77
private final static int MAX_CONNECTIONS = 128;
78
79
/**
80
* Check few more connections to make sure that MAX_CONNECTIONS are safe.
81
*/
82
private final static int CONNECTIONS_RESERVE = 10;
83
84
/**
85
* The test used to fail with connection reset by peer set to 50. (and once
86
* in a three if it was set to 10). So now we set it to MAX_CONNECTIONS
87
* (128).
88
*/
89
private final static int BACKLOG_QUEUE_LENGTH = MAX_CONNECTIONS;
90
91
/**
92
* Number of parcels to be sent/recieved.
93
*/
94
private final static int DATA_PARCELS = 128;
95
96
/**
97
* Maximal length of data parcel to be sent/recieved (it equals to 256 bytes
98
* now).
99
*/
100
private final static int MAX_PARCEL = 1 << 8;
101
102
/**
103
* Either actually display optional reports or not.
104
*/
105
static private final boolean DEBUG_MODE = false;
106
107
/**
108
* How many IP sockets can we open simultaneously? Check if
109
* <code>MAX_CONNECTIONS</code> connections can be open simultaneously.
110
*/
111
private static int detectOSLimitation() {
112
final int CONNECTIONS_TO_TRY = MAX_CONNECTIONS + CONNECTIONS_RESERVE;
113
display("--- Trying to open " + CONNECTIONS_TO_TRY + " connections:");
114
115
InetAddress address;
116
ServerSocket serverSocket;
117
try {
118
address = InetAddress.getLocalHost();
119
int anyPort = 0;
120
int defaultBacklog = BACKLOG_QUEUE_LENGTH;
121
serverSocket = new ServerSocket(anyPort, defaultBacklog, address);
122
} catch (IOException ioe) {
123
throw new Error("FATAL error while loading the test: " + ioe);
124
}
125
display(serverSocket.toString());
126
127
Socket server[] = new Socket[CONNECTIONS_TO_TRY];
128
Socket client[] = new Socket[CONNECTIONS_TO_TRY];
129
130
int i, port = serverSocket.getLocalPort();
131
for (i = 0; i < CONNECTIONS_TO_TRY; i++) {
132
try {
133
client[i] = new Socket(address, port);
134
display(">Open: client[" + i + "] = " + client[i]);
135
server[i] = serverSocket.accept();
136
display(">Open: server[" + i + "] = " + server[i]);
137
} catch (IOException ioe) {
138
display(">OOPS! -- failed to open connection #" + i);
139
break;
140
}
141
}
142
display("> Could open "
143
+ (i < CONNECTIONS_TO_TRY ? "only " : "") + i + " connections.");
144
display(">Closing them:");
145
for (int j = 0; j < i; j++) {
146
try {
147
server[j].close();
148
client[j].close();
149
} catch (IOException ioe) {
150
throw new Error("FATAL error while loading the test: " + ioe);
151
}
152
}
153
display(">OK.");
154
int safeConnections = i - CONNECTIONS_RESERVE;
155
if (safeConnections < 1) {
156
safeConnections = 1;
157
}
158
if (safeConnections < MAX_CONNECTIONS) {
159
complain("------------------------- CAUTION: -------------------");
160
complain("While checking the OS limitations, the test found that");
161
complain("only " + i + " TCP/IP socket connections could be safely open");
162
complain("simultaneously. However, possibility to open at least");
163
complain("" + MAX_CONNECTIONS + "+" + CONNECTIONS_RESERVE
164
+ " connections were expected.");
165
complain("");
166
complain("So, the test will check only " + safeConnections + " connection"
167
+ (safeConnections == 1 ? "" : "s") + " which seem");
168
complain("safe to be open simultaneously.");
169
complain("------------------------------------------------------");
170
}
171
return safeConnections;
172
}
173
174
//----------------------------------------------------------------//
175
/**
176
* Re-calls to the method <code>run(args[],out)</code> actually performing
177
* the test. After <code>run(args[],out)</code> stops, follow JDK-like
178
* convention for exit codes. I.e.: stop with exit status 95 if the test has
179
* passed, or with status 97 if the test has failed.
180
*
181
* @see #run(String[],PrintStream)
182
*/
183
public static void main(String args[]) {
184
int exitCode = run(args, System.out);
185
System.exit(exitCode + 95);
186
// JCK-like exit status.
187
}
188
189
public static int run(String args[], PrintStream out) {
190
HugeDataTransferTest.out = out;
191
192
//
193
// Get the Internet address of the local machine.
194
//
195
InetAddress address = null;
196
try {
197
address = InetAddress.getLocalHost();
198
} catch (UnknownHostException exception) {
199
complain(exception.toString());
200
return 2; // FAILED
201
}
202
display("Host: " + address);
203
204
//
205
// Detect if it is safe to open MAX_CONNETIONS simultaneously:
206
//
207
final int CONNECTIONS = detectOSLimitation();
208
209
//
210
// Assign ServerSocket, and start client VM which should open
211
// the prescribed number of CONNECTIONS to that ServerSocket.
212
//
213
ServerSocket serverSocket;
214
try {
215
final int anyPort = 0;
216
final int defaultBacklog = BACKLOG_QUEUE_LENGTH;
217
serverSocket = new ServerSocket(anyPort, defaultBacklog, address);
218
} catch (IOException exception) {
219
complain("Cannot assign a ServerSocket on: " + address);
220
return 2;
221
}
222
223
//
224
// Start the client process on different VM.
225
//
226
String jdkPath = System.getProperty("test.jdk");
227
Path toolName = Paths.get("bin", "java" + (isWindows() ? ".exe" : ""));
228
Path jdkTool = Paths.get(jdkPath, toolName.toString());
229
230
String IPAddress = address.getHostAddress();
231
int localPort = serverSocket.getLocalPort();
232
String arguments = " " + CONNECTIONS + " " + IPAddress + " " + localPort;
233
//String command = args[0] + " " + network006.class.getName() + "$Client " + arguments;
234
String command = jdkTool.toAbsolutePath().toString() + " " + Client.class.getName() + " " + arguments;
235
try {
236
SO_TIMEOUT = Integer.parseInt(args[0]) * 60 * 1000;
237
} catch (NumberFormatException e) {
238
complain("Wrong timeout argument: " + e);
239
return 2;
240
}
241
242
Runtime runtime = Runtime.getRuntime();
243
244
Process client = null;
245
IORedirector redirectOut = null;
246
IORedirector redirectErr = null;
247
248
try {
249
// Start clients on different JVM:
250
client = runtime.exec(command);
251
252
// Provide clients with access to stderr and stdout:
253
InputStream clientOut = client.getInputStream();
254
InputStream clientErr = client.getErrorStream();
255
redirectOut = new IORedirector(clientOut, DEBUG_MODE ? out : null);
256
redirectErr = new IORedirector(clientErr, out);
257
redirectOut.start();
258
redirectErr.start();
259
260
} catch (IOException exception) {
261
complain("Failed to start client: " + exception);
262
return 2;
263
}
264
//
265
// Start the server threads (and let them establish connections):
266
//
267
268
Server server[] = new Server[CONNECTIONS];
269
for (int i = 0; i < CONNECTIONS; i++) {
270
server[i] = new Server(serverSocket);
271
display("Server #" + i + ": " + server[i]);
272
server[i].start();
273
}
274
275
//
276
// Wait for the servers and the clients:
277
//
278
boolean testFailed = false;
279
280
try {
281
client.waitFor();
282
int clientStatus = client.exitValue();
283
display("Client VM exitCode=" + clientStatus);
284
285
// Let I/O redirectors to flush:
286
if (redirectOut.isAlive()) {
287
redirectOut.join();
288
}
289
if (redirectErr.isAlive()) {
290
redirectErr.join();
291
}
292
293
// If client has crashed, also terminate the server (to avoid hangup).
294
if (clientStatus != 95) {
295
complain("Client VM has crashed: exit status=" + clientStatus);
296
testFailed = true;
297
}
298
299
// Client has finished OK; wait for the server.
300
for (int i = 0; i < CONNECTIONS; i++) {
301
display("Server: waiting for #" + i);
302
if (server[i].isAlive()) {
303
display("Server #" + i + ": (joining...)" + server[i]);
304
server[i].join();
305
}
306
if (server[i].exception != null) {
307
if (server[i].message != null) {
308
complain("Server #" + i + "(finished): with message:" + server[i].message);
309
}
310
311
complain("Server #" + i + "(finished): " + server[i].exception);
312
server[i].exception.printStackTrace(out);
313
out.flush();
314
// complain("Server #"+i+": "+server[i].exception.getStackTrace());
315
testFailed = true;
316
}
317
}
318
319
} catch (InterruptedException exception) {
320
complain("Test interrupted: " + exception);
321
testFailed = true;
322
}
323
324
if (testFailed) {
325
complain("Test failed.");
326
} else {
327
display("Test passed.");
328
}
329
return testFailed ? 2 : 0;
330
}
331
332
private static boolean isWindows() {
333
return System.getProperty("os.name").toLowerCase().startsWith("win");
334
}
335
//----------------------------------------------------------------//
336
/**
337
* Log stream for error messages and/or (optional) execution trace.
338
*/
339
private static PrintStream out;
340
341
/**
342
* Print error message.
343
*/
344
private static synchronized void complain(Object message) {
345
out.println("# " + message);
346
out.flush();
347
}
348
349
;
350
351
/**
352
* Display optional report: comment ca va?
353
*/
354
private static synchronized void display(Object report) {
355
if (DEBUG_MODE) {
356
out.println(report.toString());
357
}
358
out.flush(); //todo shouldn't this be inside if??
359
}
360
361
;
362
363
//----------------------------------------------------------------//
364
365
/**
366
* Server thread should reply to data parcels sent by Client VM.
367
*/
368
private static class Server extends Thread {
369
370
/**
371
* The socket is assigned at the Server instantiation.
372
*/
373
private ServerSocket serverSocket;
374
375
/**
376
* The socket is assigned at the Server runtime.
377
*/
378
private Socket socket;
379
380
/**
381
* Display the server socket.
382
*/
383
@Override
384
public String toString() {
385
386
return "ServerSocket: " + serverSocket.toString();
387
// + " socket: " + socket.toString();
388
}
389
390
/**
391
* Which port is this socket listening?
392
*/
393
int getPort() {
394
return serverSocket.getLocalPort();
395
}
396
397
/**
398
* Find some free port at the given <code>address</code> and attach new
399
* server to hear that port. // lidsten to??
400
*/
401
public Server(ServerSocket serverSocket) {
402
this.serverSocket = serverSocket;
403
}
404
405
/**
406
* Exception just arisen while the server was working, or
407
* <code>null</code> if it was OK with the server.
408
*/
409
Exception exception = null;
410
String message = null;
411
412
/**
413
* Accept connection, then reply to client's parcels.
414
*/
415
@Override
416
public void run() {
417
try {
418
socket = serverSocket.accept();
419
socket.setSoTimeout(SO_TIMEOUT);
420
421
InputStream istream = socket.getInputStream();
422
OutputStream ostream = socket.getOutputStream();
423
424
Random random = new Random(getPort());
425
426
for (int i = 0; i < DATA_PARCELS; i++) {
427
Parcel etalon = new Parcel(random);
428
message = "reading parcel number " + i;
429
Parcel sample = new Parcel(istream); // read
430
if (!sample.equals(etalon)) {
431
complain("Server thread for port #"
432
+ getPort() + " got unexpected parcel:\n"
433
+ "sample=" + sample + "\n"
434
+ "etalon=" + etalon);
435
throw new TestFailure( //received??
436
"server has read unexpected parcel");
437
}
438
message = "sending parcel number " + i;
439
etalon.send(ostream);
440
ostream.flush();
441
}
442
443
int datum = istream.read(); // wait for client close()
444
if (datum >= 0) {
445
throw new TestFailure(
446
"server has read ambigous byte: " + datum);
447
}
448
449
ostream.close(); // implies: socket.close();
450
451
} catch (Exception oops) {
452
exception = oops;
453
}
454
}
455
}
456
457
//----------------------------------------------------------------//
458
/**
459
* Client VM should send data parcels to Server VM and recieve and verify
460
* the server's replies.
461
*/
462
private static class Client extends Thread {
463
464
/**
465
* This thread uses the single client socket.
466
*/
467
private Socket socket;
468
469
/**
470
* Address and port of this socket.
471
*/
472
@Override
473
public String toString() {
474
return socket.toString();
475
}
476
477
/**
478
* Did the thread failed? If yes, what is the failure's reason.
479
*/
480
Exception exception = null;
481
String message = null;
482
483
public static java.io.PrintStream complainStream = System.out;
484
public static java.io.PrintStream displayStream = System.err;
485
486
/**
487
* Connect client socket on the given <code>address</code> and
488
* <code>port</code>.
489
*/
490
Client(InetAddress address, int port) throws IOException {
491
socket = new Socket(address, port);
492
socket.setSoTimeout(SO_TIMEOUT);
493
}
494
495
/**
496
* What is the port number this socket is listening for?
497
*/
498
int getPort() {
499
return socket.getPort();
500
}
501
502
/**
503
* Establish connection, then read/respond <code>DATA_PARCELS</code>
504
* parcels of random data. Set initial seed for pseudo-random numbers
505
* generator to the value of the local port number.
506
*
507
* @see #DATA_PARCELS
508
* @see #getPort()
509
*/
510
@Override
511
public void run() {
512
try {
513
InputStream istream = socket.getInputStream();
514
OutputStream ostream = socket.getOutputStream();
515
516
Random random = new Random(getPort());
517
// suggested by Oleg -- to avoid race conditions
518
/* try{
519
Thread.sleep(500);
520
}
521
catch (java.lang.InterruptedException e)
522
{
523
}*/
524
525
for (int i = 0; i < DATA_PARCELS; i++) {
526
Parcel etalon = new Parcel(random);
527
message = "sending parcel number: " + i;
528
etalon.send(ostream);
529
ostream.flush();
530
531
message = "reading parcel number: " + i;
532
Parcel sample = new Parcel(istream); // read
533
if (!sample.equals(etalon)) {
534
complain("Client thread for port #"
535
+ getPort() + " got unexpected parcel:\n"
536
+ "sample=" + sample + "\n"
537
+ "etalon=" + etalon);
538
throw new TestFailure(
539
"parcel context is unexpected to client");
540
}
541
}
542
543
if (istream.available() > 0) {
544
int datum = istream.read();
545
throw new TestFailure(
546
"client has read ambigous byte: " + datum);
547
}
548
ostream.close(); // implies: socket.close()
549
550
} catch (Exception oops) {
551
exception = oops;
552
}
553
}
554
555
/**
556
* Establish lots of connections to server socket, attack servers with
557
* huge data parcels, and check if they reply correctly. The number of
558
* connections to try, the address and port number for the server socket
559
* are passed through <code>args[]</code>, like:
560
* <pre>
561
* java network006$Client connections_to_try address port
562
* </pre>
563
*/
564
public static void main(String args[]) {
565
if (DEBUG_MODE) {
566
try {
567
String filename = "Client" + ((args.length == 3) ? args[2] : "new");
568
displayStream = new PrintStream(filename + ".out");
569
complainStream = new PrintStream(filename + ".err");
570
} catch (FileNotFoundException exception) {
571
complain(exception);
572
}
573
574
}
575
576
if (args.length != 3) {
577
complain("Client expects 3 paramenets:");
578
complain(" java " + Client.class.getName() + " connections_to_try address port");
579
exit(1); // FAILED
580
}
581
582
int CONNECTIONS = Integer.parseInt(args[0]);
583
display("Client VM: will try " + CONNECTIONS + " connections.");
584
InetAddress address;
585
try {
586
address = InetAddress.getByName(args[1]);
587
} catch (UnknownHostException exception) {
588
address = null;
589
complain("Client: cannot find host: \"" + args[1] + "\"");
590
exit(4);
591
}
592
display("Client: host to contact: " + address);
593
int port = Integer.parseInt(args[2]);
594
display("Client: port to contact: " + port);
595
596
//
597
// Establish connections, and start client processes:
598
//
599
Client client[] = new Client[CONNECTIONS];
600
for (int i = 0; i < CONNECTIONS; i++) {
601
try {
602
client[i] = new Client(address, port);
603
display("Client #" + i + ": " + client[i]);
604
605
} catch (IOException ioe) {
606
complain("Client #" + i + "(creation): " + ioe);
607
ioe.printStackTrace(complainStream);
608
complainStream.flush();
609
// complain("Client #" + i + "(creation): " + ioe.getStackTrace());
610
exit(3);
611
}
612
}
613
614
for (int i = 0; i < CONNECTIONS; i++) {
615
client[i].start();
616
}
617
618
//
619
// Wait until testing is not finished:
620
//
621
int status = 0;
622
for (int i = 0; i < CONNECTIONS; i++) {
623
display("Client: waiting for #" + i);
624
if (client[i].isAlive()) {
625
display("Client #" + i + ": (joining...)" + client[i]);
626
627
try {
628
client[i].join();
629
} catch (InterruptedException ie) {
630
complain("Client #" + i + ": " + ie);
631
status = 3;
632
}
633
}
634
if (client[i].exception != null) {
635
if (client[i].message != null) {
636
complain("Client #" + i + "(finished) with message: " + client[i].message);
637
}
638
complain("Client #" + i + "(finished): " + client[i].exception);
639
client[i].exception.printStackTrace(complainStream);
640
complainStream.flush();
641
if (status == 0) {
642
status = 2;
643
}
644
}
645
}
646
647
exit(status);
648
}
649
650
/**
651
* Print error message.
652
*/
653
private static synchronized void complain(Object message) {
654
complainStream.println("# " + message);
655
complainStream.flush();
656
}
657
658
/**
659
* Display execution trace.
660
*/
661
private static synchronized void display(Object message) {
662
if (!DEBUG_MODE) {
663
return;
664
}
665
displayStream.println(message.toString());
666
displayStream.flush();
667
}
668
669
/**
670
* Exit with JCK-like status.
671
*/
672
private static void exit(int exitCode) {
673
int status = exitCode + 95;
674
// display("Client: exiting with code=" + status);
675
System.exit(status);
676
}
677
}
678
679
/**
680
* Two of such threads should redirect <code>out</code> and <code>err</code>
681
* streams of client VM.
682
*/
683
private static class IORedirector extends Thread {
684
685
/**
686
* Source stream.
687
*/
688
InputStream in;
689
/**
690
* Destination stream.
691
*/
692
OutputStream out;
693
694
/**
695
* Redirect <code>in</code> to <code>out</code>.
696
*/
697
public IORedirector(InputStream in, OutputStream out) {
698
this.in = in;
699
this.out = out;
700
}
701
702
/**
703
* Read input stream until the EOF, and write everithing to output
704
* stream. If output stream is assigned to <code>null</code>, do not
705
* print anything, but read the input stream anywhere.
706
*/
707
@Override
708
public void run() {
709
try {
710
for (;;) {
711
int symbol = in.read();
712
if (symbol < 0) {
713
break; // EOF
714
}
715
if (out != null) {
716
out.write(symbol);
717
}
718
}
719
720
if (out != null) {
721
out.flush();
722
}
723
724
} catch (IOException exception) {
725
throw new TestFailure("IORedirector exception: " + exception);
726
}
727
}
728
}
729
730
//----------------------------------------------------------------//
731
/**
732
* A data parcel to be sent/recieved between Client VM and Server thread.
733
* When data parcel is sent, first 4 bytes are transfered which encode the
734
* <code>int</code> number equal to size of the parcel minus 1. I.e.: if
735
* number of data bytes in the parcel's contents is <code>N</code>, then the
736
* first 4 bytes encode the number <code>N-1</code>. After that, the
737
* parcel's contents bytes are transered.
738
*/
739
static class Parcel {
740
741
private final byte[] parcel;
742
743
/**
744
* Display all bytes as integer values from 0 to 255; or return
745
* ``<tt>null</tt>'' if this Parcel is not yet initialized.
746
*/
747
@Override
748
public String toString() {
749
if (parcel == null) {
750
return "null";
751
}
752
String s = "{";
753
for (int i = 0; i < parcel.length; i++) {
754
s += (i > 0 ? ", " : "") + ((int) parcel[i] & 0xFF);
755
}
756
return s + "}";
757
}
758
759
/**
760
* Generate new <code>parcel[]</code> array using the given
761
* <code>random</code> numbers generator. Client and Server threads
762
* should use identical <code>random</code> generators, so that those
763
* threads could generate equal data parcels and check the parcel just
764
* transfered.
765
*/
766
public Parcel(Random random) {
767
int size = random.nextInt(MAX_PARCEL) + 1;
768
parcel = new byte[size];
769
for (int i = 0; i < size; i++) {
770
parcel[i] = (byte) random.nextInt(256);
771
}
772
}
773
774
;
775
776
/**
777
* Read exactly <code>size</code> bytes from the <code>istream</code>
778
* if possible, or throw <code>TestFailure</code> if unexpected end of
779
* <code>istream</code> occurs.
780
*/
781
private static byte[] readBytes(int size, InputStream istream)
782
throws IOException {
783
784
byte data[] = new byte[size];
785
for (int i = 0; i < size; i++) {
786
int datum = istream.read();
787
if (datum < 0) {
788
throw new TestFailure(
789
"unexpected EOF: have read: " + i + " bytes of " + size);
790
}
791
data[i] = (byte) datum;
792
}
793
return data;
794
}
795
796
/**
797
* Read 4 bytes from <code>istream</code> and threat them to encode size
798
* of data parcel following these 4 bytes.
799
*/
800
private static int getSize(InputStream istream) throws IOException {
801
byte data[] = readBytes(4, istream);
802
int data0 = (int) data[0] & 0xFF;
803
int data1 = (int) data[1] & 0xFF;
804
int data2 = (int) data[2] & 0xFF;
805
int data3 = (int) data[3] & 0xFF;
806
int sizeWord = data0 + (data1 << 8) + (data2 << 16) + (data3 << 24);
807
int size = sizeWord + 1;
808
if (size <= 0) {
809
throw new TestFailure("illegal size: " + size);
810
}
811
return size;
812
}
813
814
/**
815
* Send 4 bytes encoding actual size of the parcel just to be
816
* transfered.
817
*/
818
private static void putSize(OutputStream ostream, int size)
819
throws IOException {
820
821
if (size <= 0) {
822
throw new TestFailure("illegal size: " + size);
823
}
824
825
int sizeWord = size - 1;
826
byte data[] = new byte[4];
827
data[0] = (byte) sizeWord;
828
data[1] = (byte) (sizeWord >> 8);
829
data[2] = (byte) (sizeWord >> 16);
830
data[3] = (byte) (sizeWord >> 24);
831
ostream.write(data);
832
}
833
834
/**
835
* Recieve data parcel.
836
*/
837
public Parcel(InputStream istream) throws IOException {
838
int size = getSize(istream);
839
parcel = readBytes(size, istream);
840
}
841
842
/**
843
* Send <code>this</code> data parcel.
844
*/
845
public void send(OutputStream ostream) throws IOException {
846
int size = parcel.length;
847
putSize(ostream, size);
848
ostream.write(parcel);
849
}
850
851
/**
852
* Check byte-to-byte equality between <code>this</code> and the
853
* <code>other</code> parcels.
854
*/
855
public boolean equals(Parcel other) {
856
if (this.parcel.length != other.parcel.length) {
857
return false;
858
}
859
int size = parcel.length;
860
for (int i = 0; i < size; i++) {
861
if (this.parcel[i] != other.parcel[i]) {
862
return false;
863
}
864
}
865
return true;
866
}
867
}
868
869
/**
870
* Server or Client may throw this exception to report the test failure.
871
*/
872
static class TestFailure extends RuntimeException {
873
874
/**
875
* Report particular <code>purpose</code> of the test failure.
876
*/
877
public TestFailure(String purpose) {
878
super(purpose);
879
}
880
}
881
}
882
883