1 /*
   2  * Copyright (c) 2003, 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  *
  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 public class EchoService {
  69 
  70     private static void doIt(SocketChannel sc, int closeAfter, int delay) throws IOException {
  71         ByteBuffer bb = ByteBuffer.allocate(1024);
  72         int total = 0;
  73         for (;;) {
  74             bb.clear();
  75             int n = sc.read(bb);
  76             if (n < 0) {
  77                 break;
  78             }
  79             total += n;
  80 
  81             // echo
  82             bb.flip();
  83             sc.write(bb);
  84 
  85             // close after X bytes?
  86             if (closeAfter > 0 && total >= closeAfter) {
  87                 break;
  88             }
  89         }
  90 
  91         sc.close();
  92         if (delay > 0) {
  93             try {
  94                 Thread.currentThread().sleep(delay);
  95             } catch (InterruptedException x) { }
  96         }
  97     }
  98 
  99     private static void doIt(DatagramChannel dc) throws IOException {
 100         ByteBuffer bb = ByteBuffer.allocate(1024);
 101         SocketAddress sa = dc.receive(bb);
 102         bb.flip();
 103         dc.send(bb, sa);
 104         dc.close();
 105     }
 106 
 107 
 108     // A worker thread to service a single connection
 109     // The class maintains a count of the number of worker threads so
 110     // can the service can terminate then all clients disconnect.
 111 
 112     static class Worker implements Runnable {
 113         private static int count = 0;
 114         private static Object lock = new Object();
 115 
 116         public static int count() {
 117             synchronized (lock) {
 118                 return count;
 119             }
 120         }
 121 
 122         private SocketChannel sc;
 123 
 124         Worker(SocketChannel sc) {
 125             this.sc = sc;
 126             synchronized (lock) {
 127                 count++;
 128             }
 129         }
 130 
 131         public void run() {
 132             try {
 133                 doIt(sc, -1, -1);
 134             } catch (IOException x) {
 135             } finally {
 136                 synchronized (lock) {
 137                     count--;
 138                 }
 139             }
 140 
 141         }
 142     }
 143 
 144     public static void main(String args[]) throws IOException {
 145         Channel c = System.inheritedChannel();
 146         if (c == null) {
 147             return;
 148         }
 149 
 150         // tcp nowait
 151         if (c instanceof SocketChannel) {
 152             int closeAfter = 0;
 153             int delay = 0;
 154             if (args.length > 0) {
 155                 closeAfter = Integer.parseInt(args[0]);
 156             }
 157             if (args.length > 1) {
 158                 delay = Integer.parseInt(args[1]);
 159             }
 160             doIt((SocketChannel)c, closeAfter, delay);
 161         }
 162 
 163         // tcp wait - in this case we take over the listener socket
 164         // In this test case we create a thread to service each connection
 165         // and terminate after all clients are gone.
 166         //
 167         if (c instanceof ServerSocketChannel) {
 168             ServerSocketChannel ssc = (ServerSocketChannel)c;
 169 
 170             ssc.configureBlocking(false);
 171             Selector sel = ssc.provider().openSelector();
 172             SelectionKey sk = ssc.register(sel, SelectionKey.OP_ACCEPT);
 173             SocketChannel sc;
 174             int count = 0;
 175             for (;;) {
 176                  sel.select(5000);
 177                  if (sk.isAcceptable() && ((sc = ssc.accept()) != null)) {
 178                     Worker w = new Worker(sc);
 179                     (new Thread(w)).start();
 180                  } else {
 181                      // if all clients have disconnected then we die as well.
 182                      if (Worker.count() == 0) {
 183                         break;
 184                      }
 185                  }
 186             }
 187             ssc.close();
 188         }
 189 
 190         // udp wait
 191         if (c instanceof DatagramChannel) {
 192             doIt((DatagramChannel)c);
 193         }
 194 
 195         // linger?
 196         if (args.length > 0) {
 197             int delay = Integer.parseInt(args[0]);
 198             try {
 199                 Thread.currentThread().sleep(delay);
 200             } catch (InterruptedException x) { }
 201         }
 202 
 203     }
 204 
 205 }