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