1 /*
   2  * Copyright (c) 2003, 2004, 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.nio.*;
  59 import java.nio.channels.*;
  60 import java.io.IOException;
  61 import java.net.*;







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