1 /*
   2  * Copyright (c) 2003, 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  *
  27  * An "echo" service designed to be used with inetd. It can be configured in
  28  * inetd.conf to be used by any of the following types of services :-
  29  *
  30  *      stream  tcp   nowait
  31  *      stream  tcp6  nowait
  32  *      stream  tcp   wait
  33  *      stream  tcp6  wait
  34  *      dgram   udp   wait
  35  *      dgram   udp6  wait
  36  *
  37  * If configured as a "tcp nowait" service then inetd will launch a
  38  * VM to run the EchoService each time that a client connects to
  39  * the TCP port. The EchoService simply echos any messages it
  40  * receives from the client and shuts if the client closes the
  41  * connection.
  42  *
  43  * If configured as a "tcp wait" service then inetd will launch a VM
  44  * to run the EchoService when a client connects to the port. When
  45  * launched the EchoService takes over the listener socket. It
  46  * terminates when all clients have disconnected and the service
  47  * is idle for a few seconds.
  48  *
  49  * If configured as a "udp wait" service then a VM will be launched for
  50  * each UDP packet to the configured port. System.inheritedChannel()
  51  * will return a DatagramChannel. The echo service here will terminate after
  52  * echoing the UDP packet back to the client.
  53  *
  54  * The service closes the inherited network channel when complete. To
  55  * facilate testing that the channel is closed the "tcp nowait" service
  56  * can close the connection after a given number of bytes.
  57  */
  58 import java.io.IOException;
  59 import java.net.SocketAddress;
  60 import java.nio.ByteBuffer;
  61 import java.nio.channels.Channel;
  62 import java.nio.channels.DatagramChannel;
  63 import java.nio.channels.SelectionKey;
  64 import java.nio.channels.Selector;
  65 import java.nio.channels.ServerSocketChannel;
  66 import java.nio.channels.SocketChannel;
  67 
  68 import jdk.test.lib.Utils;
  69 
  70 public class EchoService {
  71 
  72     private static void doIt(SocketChannel sc, int closeAfter, int delay) throws IOException {
  73         ByteBuffer bb = ByteBuffer.allocate(1024);
  74         int total = 0;
  75         for (;;) {
  76             bb.clear();
  77             int n = sc.read(bb);
  78             if (n < 0) {
  79                 break;
  80             }
  81             total += n;
  82 
  83             // echo
  84             bb.flip();
  85             sc.write(bb);
  86 
  87             // close after X bytes?
  88             if (closeAfter > 0 && total >= closeAfter) {
  89                 break;
  90             }
  91         }
  92 
  93         sc.close();
  94         if (delay > 0) {
  95             try {
  96                 Thread.currentThread().sleep(delay);
  97             } catch (InterruptedException x) { }
  98         }
  99     }
 100 
 101     private static void doIt(DatagramChannel dc) throws IOException {
 102         ByteBuffer bb = ByteBuffer.allocate(1024);
 103         SocketAddress sa = dc.receive(bb);
 104         bb.flip();
 105         dc.send(bb, sa);
 106         dc.close();
 107     }
 108 
 109 
 110     // A worker thread to service a single connection
 111     // The class maintains a count of the number of worker threads so
 112     // can the service can terminate then all clients disconnect.
 113 
 114     static class Worker implements Runnable {
 115         private static int count = 0;
 116         private static Object lock = new Object();
 117 
 118         public static int count() {
 119             synchronized (lock) {
 120                 return count;
 121             }
 122         }
 123 
 124         private SocketChannel sc;
 125 
 126         Worker(SocketChannel sc) {
 127             this.sc = sc;
 128             synchronized (lock) {
 129                 count++;
 130             }
 131         }
 132 
 133         public void run() {
 134             try {
 135                 doIt(sc, -1, -1);
 136             } catch (IOException x) {
 137             } finally {
 138                 synchronized (lock) {
 139                     count--;
 140                 }
 141             }
 142 
 143         }
 144     }
 145 
 146     public static void main(String args[]) throws IOException {
 147         Channel c = System.inheritedChannel();
 148         if (c == null) {
 149             return;
 150         }
 151 
 152         // tcp nowait
 153         if (c instanceof SocketChannel) {
 154             int closeAfter = 0;
 155             int delay = 0;
 156             if (args.length > 0) {
 157                 closeAfter = Integer.parseInt(args[0]);
 158             }
 159             if (args.length > 1) {
 160                 delay = Integer.parseInt(args[1]);
 161             }
 162             doIt((SocketChannel)c, closeAfter, delay);
 163         }
 164 
 165         // tcp wait - in this case we take over the listener socket
 166         // In this test case we create a thread to service each connection
 167         // and terminate after all clients are gone.
 168         //
 169         if (c instanceof ServerSocketChannel) {
 170             ServerSocketChannel ssc = (ServerSocketChannel)c;
 171 
 172             ssc.configureBlocking(false);
 173             Selector sel = ssc.provider().openSelector();
 174             SelectionKey sk = ssc.register(sel, SelectionKey.OP_ACCEPT);
 175             SocketChannel sc;
 176             int count = 0;
 177             for (;;) {
 178                  sel.select((int)Utils.adjustTimeout(5000));
 179                  if (sk.isAcceptable() && ((sc = ssc.accept()) != null)) {
 180                     Worker w = new Worker(sc);
 181                     (new Thread(w)).start();
 182                  } else {
 183                      // if all clients have disconnected then we die as well.
 184                      if (Worker.count() == 0) {
 185                         break;
 186                      }
 187                  }
 188             }
 189             ssc.close();
 190         }
 191 
 192         // udp wait
 193         if (c instanceof DatagramChannel) {
 194             doIt((DatagramChannel)c);
 195         }
 196 
 197         // linger?
 198         if (args.length > 0) {
 199             int delay = Integer.parseInt(args[0]);
 200             try {
 201                 Thread.currentThread().sleep(delay);
 202             } catch (InterruptedException x) { }
 203         }
 204 
 205     }
 206 
 207 }