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 
doIt(SocketChannel sc, int closeAfter, int delay)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 
doIt(DatagramChannel dc)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 
count()111         public static int count() {
112             synchronized (lock) {
113                 return count;
114             }
115         }
116 
117         private SocketChannel sc;
118 
Worker(SocketChannel sc)119         Worker(SocketChannel sc) {
120             this.sc = sc;
121             synchronized (lock) {
122                 count++;
123             }
124         }
125 
run()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 
main(String args[])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 }
201