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