test/java/nio/channels/TestUtil.java

Print this page


   1 /*
   2  * Copyright (c) 2000, 2008, 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 /* Test utilities
  25  *
  26  */
  27 
  28 import java.io.*;
  29 import java.net.*;
  30 import java.nio.*;
  31 import java.nio.channels.*;



  32 import java.util.Random;
  33 
  34 
  35 public class TestUtil {
  36 
  37     // Test hosts used by the channels tests - change these when
  38     // executing in a different network.
  39     public static final String HOST = "javaweb.sfbay.sun.com";
  40     public static final String REFUSING_HOST = "jano1.sfbay.sun.com";
  41     public static final String FAR_HOST = "irejano.ireland.sun.com";
  42     public static final String UNRESOLVABLE_HOST = "blah-blah.blah-blah.blah";
  43 
  44     private TestUtil() { }
  45 
  46     // Repeatedly try random ports until we bind to one.  You might be tempted
  47     // to do this:
  48     //
  49     //     ServerSocketChannel ssc = ServerSocketChannel.open();
  50     //     ssc.socket().bind(new InetSocketAddress(0));
  51     //     SocketAddress sa = ssc.socket().getLocalSocketAddress();
  52     //
  53     // but unfortunately it doesn't work on NT 4.0.
  54     //
  55     // Returns the bound port.
  56     //
  57     static int bind(ServerSocketChannel ssc) throws IOException {
  58         InetAddress lh = InetAddress.getLocalHost();
  59         Random r = new Random();
  60         for (;;) {
  61             int p = r.nextInt((1 << 16) - 1024) + 1024;


  71 
  72     // A more convenient form of bind(ServerSocketChannel) that returns a full
  73     // socket address.
  74     //
  75     static InetSocketAddress bindToRandomPort(ServerSocketChannel ssc)
  76         throws IOException
  77     {
  78         int p = bind(ssc);
  79         return new InetSocketAddress(InetAddress.getLocalHost(), p);
  80     }
  81 
  82     private static String osName = System.getProperty("os.name");
  83 
  84     static boolean onSolaris() {
  85         return osName.startsWith("SunOS");
  86     }
  87 
  88     static boolean onWindows() {
  89         return osName.startsWith("Windows");
  90     }











































































































































































































































































































































































































































































































































































































































































































































































































































  91 }
   1 /*
   2  * Copyright (c) 2000, 2012, 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 /* Test utilities
  25  *
  26  */
  27 
  28 import java.io.*;
  29 import java.net.*;

  30 import java.nio.channels.*;
  31 import java.util.ArrayList;
  32 import java.util.Date;
  33 import java.util.List;
  34 import java.util.Random;
  35 
  36 
  37 public class TestUtil {
  38 
  39     // Test hosts used by the channels tests - change these when
  40     // executing in a different network.



  41     public static final String UNRESOLVABLE_HOST = "blah-blah.blah-blah.blah";
  42 
  43     private TestUtil() { }
  44 
  45     // Repeatedly try random ports until we bind to one.  You might be tempted
  46     // to do this:
  47     //
  48     //     ServerSocketChannel ssc = ServerSocketChannel.open();
  49     //     ssc.socket().bind(new InetSocketAddress(0));
  50     //     SocketAddress sa = ssc.socket().getLocalSocketAddress();
  51     //
  52     // but unfortunately it doesn't work on NT 4.0.
  53     //
  54     // Returns the bound port.
  55     //
  56     static int bind(ServerSocketChannel ssc) throws IOException {
  57         InetAddress lh = InetAddress.getLocalHost();
  58         Random r = new Random();
  59         for (;;) {
  60             int p = r.nextInt((1 << 16) - 1024) + 1024;


  70 
  71     // A more convenient form of bind(ServerSocketChannel) that returns a full
  72     // socket address.
  73     //
  74     static InetSocketAddress bindToRandomPort(ServerSocketChannel ssc)
  75         throws IOException
  76     {
  77         int p = bind(ssc);
  78         return new InetSocketAddress(InetAddress.getLocalHost(), p);
  79     }
  80 
  81     private static String osName = System.getProperty("os.name");
  82 
  83     static boolean onSolaris() {
  84         return osName.startsWith("SunOS");
  85     }
  86 
  87     static boolean onWindows() {
  88         return osName.startsWith("Windows");
  89     }
  90 
  91     /**
  92      * An abstract server identifies a server which listens on a port on on a
  93      * given machine.
  94      */
  95     static abstract class AbstractServer {
  96 
  97         private AbstractServer() {
  98         }
  99 
 100         public abstract int getPort();
 101 
 102         public abstract InetAddress getAddress();
 103     }
 104 
 105     /**
 106      * A downgraded type of AbstractServer which will refuse connections. Note:
 107      * use it once and throw it away - this implementation opens an anonymous
 108      * socket and closes it, returning the address of the closed socket. If
 109      * other servers are started afterwards, the address/port might get reused
 110      * and become connectable again - so it's not a good idea to assume that
 111      * connections using this address/port will always be refused. Connections
 112      * will be refused as long as the address/port of the refusing server has
 113      * not been reused.
 114      */
 115     static class RefusingServer extends AbstractServer {
 116 
 117         final InetAddress address;
 118         final int port;
 119 
 120         private RefusingServer(InetAddress address, int port) {
 121             this.address = address;
 122             this.port = port;
 123         }
 124 
 125         @Override
 126         public int getPort() {
 127             return port;
 128         }
 129 
 130         @Override
 131         public InetAddress getAddress() {
 132             return address;
 133         }
 134 
 135         public static RefusingServer startNewServer() throws IOException {
 136             ServerSocket socket = new ServerSocket(0, 100,
 137                     InetAddress.getLocalHost());
 138             RefusingServer server = new RefusingServer(socket.getInetAddress(),
 139                     socket.getLocalPort());
 140             socket.close();
 141             return server;
 142         }
 143     }
 144 
 145     /**
 146      * An abstract class for implementing small TCP servers for the nio tests
 147      * purposes. Disclaimer: This is a naive implementation that uses the old
 148      * networking APIs (not those from {@code java.nio.*}) and shamelessly
 149      * extends/creates Threads instead of using an executor service.
 150      */
 151     static abstract class AbstractTcpServer extends AbstractServer
 152             implements Runnable, Closeable {
 153 
 154         protected final long linger; // #of ms to wait before responding
 155         private Thread acceptThread; // thread waiting for accept
 156         // list of opened connections that should be closed on close.
 157         private List<TcpConnectionThread> connections = new ArrayList<>();
 158         private ServerSocket serverSocket; // the server socket
 159         private boolean started = false; // whether the server is started
 160         Throwable error = null;
 161 
 162         /**
 163          * Creates a new abstract TCP server.
 164          *
 165          * @param linger the amount of time the server should wait before
 166          * responding to requests.
 167          */
 168         protected AbstractTcpServer(long linger) {
 169             this.linger = linger;
 170         }
 171 
 172         /**
 173          * The local port to which the server is bound.
 174          *
 175          * @return The local port to which the server is bound.
 176          * @exception IllegalStateException is thrown if the server is not
 177          * started.
 178          */
 179         @Override
 180         public final synchronized int getPort() {
 181             if (!started) {
 182                 throw new IllegalStateException("Not started");
 183             }
 184             return serverSocket.getLocalPort();
 185         }
 186 
 187         /**
 188          * The local address to which the server is bound.
 189          *
 190          * @return The local address to which the server is bound.
 191          * @exception IllegalStateException is thrown if the server is not
 192          * started.
 193          */
 194         @Override
 195         public final synchronized InetAddress getAddress() {
 196             if (!started) {
 197                 throw new IllegalStateException("Not started");
 198             }
 199             return serverSocket.getInetAddress();
 200         }
 201 
 202         /**
 203          * Tells whether the server is started.
 204          *
 205          * @return true if the server is started.
 206          */
 207         public final synchronized boolean isStarted() {
 208             return started;
 209         }
 210 
 211         /**
 212          * Creates a new server socket.
 213          *
 214          * @param port local port to bind to.
 215          * @param backlog requested maximum length of the queue of incoming
 216          * connections.
 217          * @param address local address to bind to.
 218          * @return a new bound server socket ready to accept connections.
 219          * @throws IOException if the socket cannot be created or bound.
 220          */
 221         protected ServerSocket newServerSocket(int port, int backlog,
 222                 InetAddress address)
 223                 throws IOException {
 224             return new ServerSocket(port, backlog, address);
 225         }
 226 
 227         /**
 228          * Starts listening for connections.
 229          *
 230          * @throws IOException if the server socket cannot be created or bound.
 231          */
 232         public final synchronized void start() throws IOException {
 233             if (started) {
 234                 return;
 235             }
 236             final ServerSocket socket =
 237                     newServerSocket(0, 100, InetAddress.getLocalHost());
 238             serverSocket = socket;
 239             acceptThread = new Thread(this);
 240             acceptThread.setDaemon(true);
 241             acceptThread.start();
 242             started = true;
 243         }
 244 
 245         /**
 246          * Calls {@code Thread.sleep(linger);}
 247          */
 248         protected final void lingerIfRequired() {
 249             if (linger > 0) {
 250                 try {
 251                     Thread.sleep(linger);
 252                 } catch (InterruptedException x) {
 253                     Thread.interrupted();
 254                     final ServerSocket socket = serverSocket();
 255                     if (socket != null && !socket.isClosed()) {
 256                         System.err.println("Thread interrupted...");
 257                     }
 258                 }
 259             }
 260         }
 261 
 262         final synchronized ServerSocket serverSocket() {
 263             return this.serverSocket;
 264         }
 265 
 266         /**
 267          * The main accept loop.
 268          */
 269         @Override
 270         public final void run() {
 271             final ServerSocket sSocket = serverSocket();
 272             try {
 273                 Socket s;
 274                 while (isStarted() && !Thread.interrupted()
 275                         && (s = sSocket.accept()) != null) {
 276                     lingerIfRequired();
 277                     listen(s);
 278                 }
 279             } catch (Exception x) {
 280                 error = x;
 281             } finally {
 282                 synchronized (this) {
 283                     if (!sSocket.isClosed()) {
 284                         try {
 285                             sSocket.close();
 286                         } catch (IOException x) {
 287                             System.err.println("Failed to close server socket");
 288                         }
 289                     }
 290                     if (started && this.serverSocket == sSocket) {
 291                         started = false;
 292                         this.serverSocket = null;
 293                         this.acceptThread = null;
 294                     }
 295                 }
 296             }
 297         }
 298 
 299         /**
 300          * Represents a connection accepted by the server.
 301          */
 302         protected abstract class TcpConnectionThread extends Thread {
 303 
 304             protected final Socket socket;
 305 
 306             protected TcpConnectionThread(Socket socket) {
 307                 this.socket = socket;
 308                 this.setDaemon(true);
 309             }
 310 
 311             public void close() throws IOException {
 312                 socket.close();
 313                 interrupt();
 314             }
 315         }
 316 
 317         /**
 318          * Creates a new TcpConnnectionThread to handle the connection through
 319          * an accepted socket.
 320          *
 321          * @param s the socket returned by {@code serverSocket.accept()}.
 322          * @return a new TcpConnnectionThread to handle the connection through
 323          * an accepted socket.
 324          */
 325         protected abstract TcpConnectionThread createConnection(Socket s);
 326 
 327         /**
 328          * Creates and starts a new TcpConnectionThread to handle the accepted
 329          * socket.
 330          *
 331          * @param s the socket returned by {@code serverSocket.accept()}.
 332          */
 333         private synchronized void listen(Socket s) {
 334             TcpConnectionThread c = createConnection(s);
 335             c.start();
 336             addConnection(c);
 337         }
 338 
 339         /**
 340          * Add the connection to the list of accepted connections.
 341          *
 342          * @param connection an accepted connection.
 343          */
 344         protected synchronized void addConnection(
 345                 TcpConnectionThread connection) {
 346             connections.add(connection);
 347         }
 348 
 349         /**
 350          * Remove the connection from the list of accepted connections.
 351          *
 352          * @param connection an accepted connection.
 353          */
 354         protected synchronized void removeConnection(
 355                 TcpConnectionThread connection) {
 356             connections.remove(connection);
 357         }
 358 
 359         /**
 360          * Close the server socket and all the connections present in the list
 361          * of accepted connections.
 362          *
 363          * @throws IOException
 364          */
 365         @Override
 366         public synchronized void close() throws IOException {
 367             if (serverSocket != null && !serverSocket.isClosed()) {
 368                 serverSocket.close();
 369             }
 370             if (acceptThread != null) {
 371                 acceptThread.interrupt();
 372             }
 373             int failed = 0;
 374             for (TcpConnectionThread c : connections) {
 375                 try {
 376                     c.close();
 377                 } catch (IOException x) {
 378                     // no matter - we're closing.
 379                     failed++;
 380                 }
 381             }
 382             connections.clear();
 383             if (failed > 0) {
 384                 throw new IOException("Failed to close some connections");
 385             }
 386         }
 387     }
 388 
 389     /**
 390      * A small TCP Server that emulates the echo service for tests purposes. See
 391      * http://en.wikipedia.org/wiki/Echo_Protocol This server uses an anonymous
 392      * port - NOT the standard port 7. We don't guarantee that its behavior
 393      * exactly matches the RFC - the only purpose of this server is to have
 394      * something that responds to nio tests...
 395      */
 396     static final class EchoServer extends AbstractTcpServer {
 397 
 398         public EchoServer() {
 399             this(0L);
 400         }
 401 
 402         public EchoServer(long linger) {
 403             super(linger);
 404         }
 405 
 406         @Override
 407         protected TcpConnectionThread createConnection(Socket s) {
 408             return new EchoConnection(s);
 409         }
 410 
 411         private final class EchoConnection extends TcpConnectionThread {
 412 
 413             public EchoConnection(Socket socket) {
 414                 super(socket);
 415             }
 416 
 417             @Override
 418             public void run() {
 419                 try {
 420                     final InputStream is = socket.getInputStream();
 421                     final OutputStream out = socket.getOutputStream();
 422                     byte[] b = new byte[255];
 423                     int n;
 424                     while ((n = is.read(b)) > 0) {
 425                         lingerIfRequired();
 426                         out.write(b, 0, n);
 427                     }
 428                 } catch (IOException io) {
 429                     // fall through to finally
 430                 } finally {
 431                     if (!socket.isClosed()) {
 432                         try {
 433                             socket.close();
 434                         } catch (IOException x) {
 435                             System.err.println(
 436                                     "Failed to close echo connection socket");
 437                         }
 438                     }
 439                     removeConnection(this);
 440                 }
 441             }
 442         }
 443 
 444         public static EchoServer startNewServer() throws IOException {
 445             return startNewServer(0);
 446         }
 447 
 448         public static EchoServer startNewServer(long linger) throws IOException {
 449             final EchoServer echoServer = new EchoServer(linger);
 450             echoServer.start();
 451             return echoServer;
 452         }
 453     }
 454 
 455     /**
 456      * A small TCP server that emulates the Day & Time service for tests
 457      * purposes. See http://en.wikipedia.org/wiki/Daytime_Protocol This server
 458      * uses an anonymous port - NOT the standard port 13. We don't guarantee
 459      * that its behavior exactly matches the RFC - the only purpose of this
 460      * server is to have something that responds to nio tests...
 461      */
 462     static final class DayTimeServer extends AbstractTcpServer {
 463 
 464         public DayTimeServer() {
 465             this(0L);
 466         }
 467 
 468         public DayTimeServer(long linger) {
 469             super(linger);
 470         }
 471 
 472         @Override
 473         protected TcpConnectionThread createConnection(Socket s) {
 474             return new DayTimeServerConnection(s);
 475         }
 476 
 477         @Override
 478         protected void addConnection(TcpConnectionThread connection) {
 479             // do nothing - the connection just write the date and terminates.
 480         }
 481 
 482         @Override
 483         protected void removeConnection(TcpConnectionThread connection) {
 484             // do nothing - we're not adding connections to the list...
 485         }
 486 
 487         private final class DayTimeServerConnection extends TcpConnectionThread {
 488 
 489             public DayTimeServerConnection(Socket socket) {
 490                 super(socket);
 491             }
 492 
 493             @Override
 494             public void run() {
 495                 try {
 496                     final OutputStream out = socket.getOutputStream();
 497                     lingerIfRequired();
 498                     out.write(new Date(System.currentTimeMillis())
 499                             .toString().getBytes("US-ASCII"));
 500                     out.flush();
 501                 } catch (IOException io) {
 502                     // fall through to finally
 503                 } finally {
 504                     if (!socket.isClosed()) {
 505                         try {
 506                             socket.close();
 507                         } catch (IOException x) {
 508                             System.err.println(
 509                                     "Failed to close echo connection socket");
 510                         }
 511                     }
 512                 }
 513             }
 514         }
 515 
 516         public static DayTimeServer startNewServer()
 517                 throws IOException {
 518             return startNewServer(0);
 519         }
 520 
 521         public static DayTimeServer startNewServer(long linger)
 522                 throws IOException {
 523             final DayTimeServer daytimeServer = new DayTimeServer(linger);
 524             daytimeServer.start();
 525             return daytimeServer;
 526         }
 527     }
 528 
 529     /**
 530      * An abstract class for implementing small UDP Servers for the nio tests
 531      * purposes. Disclaimer: This is a naive implementation that uses the old
 532      * networking APIs (not those from {@code java.nio.*}) and shamelessly
 533      * extends/creates Threads instead of using an executor service.
 534      */
 535     static abstract class AbstractUdpServer extends AbstractServer
 536             implements Runnable, Closeable {
 537 
 538         protected final long linger; // #of ms to wait before responding
 539         private Thread acceptThread; // thread waiting for packets
 540         private DatagramSocket serverSocket; // the server socket
 541         private boolean started = false; // whether the server is started
 542         Throwable error = null;
 543 
 544         /**
 545          * Creates a new abstract UDP server.
 546          *
 547          * @param linger the amount of time the server should wait before
 548          * responding to requests.
 549          */
 550         protected AbstractUdpServer(long linger) {
 551             this.linger = linger;
 552         }
 553 
 554         /**
 555          * The local port to which the server is bound.
 556          *
 557          * @return The local port to which the server is bound.
 558          * @exception IllegalStateException is thrown if the server is not
 559          * started.
 560          */
 561         @Override
 562         public final synchronized int getPort() {
 563             if (!started) {
 564                 throw new IllegalStateException("Not started");
 565             }
 566             return serverSocket.getLocalPort();
 567         }
 568 
 569         /**
 570          * The local address to which the server is bound.
 571          *
 572          * @return The local address to which the server is bound.
 573          * @exception IllegalStateException is thrown if the server is not
 574          * started.
 575          */
 576         @Override
 577         public final synchronized InetAddress getAddress() {
 578             if (!started) {
 579                 throw new IllegalStateException("Not started");
 580             }
 581             return serverSocket.getLocalAddress();
 582         }
 583 
 584         /**
 585          * Tells whether the server is started.
 586          *
 587          * @return true if the server is started.
 588          */
 589         public final synchronized boolean isStarted() {
 590             return started;
 591         }
 592 
 593         /**
 594          * Creates a new datagram socket.
 595          *
 596          * @param port local port to bind to.
 597          * @param address local address to bind to.
 598          * @return a new bound server socket ready to listen for packets.
 599          * @throws IOException if the socket cannot be created or bound.
 600          */
 601         protected DatagramSocket newDatagramSocket(int port,
 602                 InetAddress address)
 603                 throws IOException {
 604             return new DatagramSocket(port, address);
 605         }
 606 
 607         /**
 608          * Starts listening for connections.
 609          *
 610          * @throws IOException if the server socket cannot be created or bound.
 611          */
 612         public final synchronized void start() throws IOException {
 613             if (started) {
 614                 return;
 615             }
 616             final DatagramSocket socket =
 617                     newDatagramSocket(0, InetAddress.getLocalHost());
 618             serverSocket = socket;
 619             acceptThread = new Thread(this);
 620             acceptThread.setDaemon(true);
 621             acceptThread.start();
 622             started = true;
 623         }
 624 
 625         /**
 626          * Calls {@code Thread.sleep(linger);}
 627          */
 628         protected final void lingerIfRequired() {
 629             if (linger > 0) {
 630                 try {
 631                     Thread.sleep(linger);
 632                 } catch (InterruptedException x) {
 633                     Thread.interrupted();
 634                     final DatagramSocket socket = serverSocket();
 635                     if (socket != null && !socket.isClosed()) {
 636                         System.err.println("Thread interrupted...");
 637                     }
 638                 }
 639             }
 640         }
 641 
 642         final synchronized DatagramSocket serverSocket() {
 643             return this.serverSocket;
 644         }
 645 
 646         final synchronized boolean send(DatagramSocket socket,
 647                 DatagramPacket response) throws IOException {
 648             if (!socket.isClosed()) {
 649                 socket.send(response);
 650                 return true;
 651             } else {
 652                 return false;
 653             }
 654         }
 655 
 656         /**
 657          * The main receive loop.
 658          */
 659         @Override
 660         public final void run() {
 661             final DatagramSocket sSocket = serverSocket();
 662             try {
 663                 final int size = Math.max(1024, sSocket.getReceiveBufferSize());
 664                 if (size > sSocket.getReceiveBufferSize()) {
 665                     sSocket.setReceiveBufferSize(size);
 666                 }
 667                 while (isStarted() && !Thread.interrupted() && !sSocket.isClosed()) {
 668                     final byte[] buf = new byte[size];
 669                     final DatagramPacket packet =
 670                             new DatagramPacket(buf, buf.length);
 671                     lingerIfRequired();
 672                     sSocket.receive(packet);
 673                     //System.out.println("Received packet from: "
 674                     //        + packet.getAddress()+":"+packet.getPort());
 675                     handle(sSocket, packet);
 676                 }
 677             } catch (Exception x) {
 678                 error = x;
 679             } finally {
 680                 synchronized (this) {
 681                     if (!sSocket.isClosed()) {
 682                         sSocket.close();
 683                     }
 684                     if (started && this.serverSocket == sSocket) {
 685                         started = false;
 686                         this.serverSocket = null;
 687                         this.acceptThread = null;
 688                     }
 689                 }
 690             }
 691         }
 692 
 693         /**
 694          * Represents an UDP request received by the server.
 695          */
 696         protected abstract class UdpRequestThread extends Thread {
 697 
 698             protected final DatagramPacket request;
 699             protected final DatagramSocket socket;
 700 
 701             protected UdpRequestThread(DatagramSocket socket, DatagramPacket request) {
 702                 this.socket = socket;
 703                 this.request = request;
 704                 this.setDaemon(true);
 705             }
 706         }
 707 
 708         /**
 709          * Creates a new UdpRequestThread to handle a DatagramPacket received
 710          * through a DatagramSocket.
 711          *
 712          * @param socket the socket through which the request was received.
 713          * @param request the datagram packet received through the socket.
 714          * @return a new UdpRequestThread to handle the request received through
 715          * a DatagramSocket.
 716          */
 717         protected abstract UdpRequestThread createConnection(DatagramSocket socket,
 718                 DatagramPacket request);
 719 
 720         /**
 721          * Creates and starts a new UdpRequestThread to handle the received
 722          * datagram packet.
 723          *
 724          * @param socket the socket through which the request was received.
 725          * @param request the datagram packet received through the socket.
 726          */
 727         private synchronized void handle(DatagramSocket socket,
 728                 DatagramPacket request) {
 729             UdpRequestThread c = createConnection(socket, request);
 730             // c can be null if the request requires no response.
 731             if (c != null) {
 732                 c.start();
 733             }
 734         }
 735 
 736         /**
 737          * Close the server socket.
 738          *
 739          * @throws IOException
 740          */
 741         @Override
 742         public synchronized void close() throws IOException {
 743             if (serverSocket != null && !serverSocket.isClosed()) {
 744                 serverSocket.close();
 745             }
 746             if (acceptThread != null) {
 747                 acceptThread.interrupt();
 748             }
 749         }
 750     }
 751 
 752     /**
 753      * A small UDP Server that emulates the discard service for tests purposes.
 754      * See http://en.wikipedia.org/wiki/Discard_Protocol This server uses an
 755      * anonymous port - NOT the standard port 9. We don't guarantee that its
 756      * behavior exactly matches the RFC - the only purpose of this server is to
 757      * have something that responds to nio tests...
 758      */
 759     static final class UdpDiscardServer extends AbstractUdpServer {
 760 
 761         public UdpDiscardServer() {
 762             this(0L);
 763         }
 764 
 765         public UdpDiscardServer(long linger) {
 766             super(linger);
 767         }
 768 
 769         @Override
 770         protected UdpRequestThread createConnection(DatagramSocket socket,
 771                 DatagramPacket request) {
 772             // no response required
 773             return null;
 774         }
 775 
 776         public static UdpDiscardServer startNewServer() throws IOException {
 777             return startNewServer(0);
 778         }
 779 
 780         public static UdpDiscardServer startNewServer(long linger) throws IOException {
 781             final UdpDiscardServer discardServer = new UdpDiscardServer(linger);
 782             discardServer.start();
 783             return discardServer;
 784         }
 785     }
 786 
 787     /**
 788      * A small UDP Server that emulates the echo service for tests purposes. See
 789      * http://en.wikipedia.org/wiki/Echo_Protocol This server uses an anonymous
 790      * port - NOT the standard port 7. We don't guarantee that its behavior
 791      * exactly matches the RFC - the only purpose of this server is to have
 792      * something that responds to nio tests...
 793      */
 794     static final class UdpEchoServer extends AbstractUdpServer {
 795 
 796         public UdpEchoServer() {
 797             this(0L);
 798         }
 799 
 800         public UdpEchoServer(long linger) {
 801             super(linger);
 802         }
 803 
 804         @Override
 805         protected UdpEchoRequest createConnection(DatagramSocket socket,
 806                 DatagramPacket request) {
 807             return new UdpEchoRequest(socket, request);
 808         }
 809 
 810         private final class UdpEchoRequest extends UdpRequestThread {
 811 
 812             public UdpEchoRequest(DatagramSocket socket, DatagramPacket request) {
 813                 super(socket, request);
 814             }
 815 
 816             @Override
 817             public void run() {
 818                 try {
 819                     lingerIfRequired();
 820                     final DatagramPacket response =
 821                             new DatagramPacket(request.getData(),
 822                                     request.getOffset(), request.getLength(),
 823                                     request.getAddress(), request.getPort());
 824                     send(socket, response);
 825                 } catch (IOException io) {
 826                     System.err.println("Failed to send response: " + io);
 827                     io.printStackTrace(System.err);
 828                 }
 829             }
 830         }
 831 
 832         public static UdpEchoServer startNewServer() throws IOException {
 833             return startNewServer(0);
 834         }
 835 
 836         public static UdpEchoServer startNewServer(long linger) throws IOException {
 837             final UdpEchoServer echoServer = new UdpEchoServer(linger);
 838             echoServer.start();
 839             return echoServer;
 840         }
 841     }
 842 
 843     /**
 844      * A small UDP server that emulates the Day & Time service for tests
 845      * purposes. See http://en.wikipedia.org/wiki/Daytime_Protocol This server
 846      * uses an anonymous port - NOT the standard port 13. We don't guarantee
 847      * that its behavior exactly matches the RFC - the only purpose of this
 848      * server is to have something that responds to nio tests...
 849      */
 850     static final class UdpDayTimeServer extends AbstractUdpServer {
 851 
 852         public UdpDayTimeServer() {
 853             this(0L);
 854         }
 855 
 856         public UdpDayTimeServer(long linger) {
 857             super(linger);
 858         }
 859 
 860         @Override
 861         protected UdpDayTimeRequestThread createConnection(DatagramSocket socket,
 862                 DatagramPacket request) {
 863             return new UdpDayTimeRequestThread(socket, request);
 864         }
 865 
 866         private final class UdpDayTimeRequestThread extends UdpRequestThread {
 867 
 868             public UdpDayTimeRequestThread(DatagramSocket socket,
 869                     DatagramPacket request) {
 870                 super(socket, request);
 871             }
 872 
 873             @Override
 874             public void run() {
 875                 try {
 876                     lingerIfRequired();
 877                     final byte[] data = new Date(System.currentTimeMillis())
 878                             .toString().getBytes("US-ASCII");
 879                     final DatagramPacket response =
 880                             new DatagramPacket(data, 0, data.length,
 881                                     request.getAddress(), request.getPort());
 882                     send(socket, response);
 883                 } catch (IOException io) {
 884                     System.err.println("Failed to send response: " + io);
 885                     io.printStackTrace(System.err);
 886                 }
 887             }
 888         }
 889 
 890         public static UdpDayTimeServer startNewServer() throws IOException {
 891             return startNewServer(0);
 892         }
 893 
 894         public static UdpDayTimeServer startNewServer(long linger)
 895                 throws IOException {
 896             final UdpDayTimeServer echoServer = new UdpDayTimeServer(linger);
 897             echoServer.start();
 898             return echoServer;
 899         }
 900     }
 901 }