View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.net.daytime;
19  
20  import java.io.IOException;
21  import java.io.PrintWriter;
22  import java.net.InetAddress;
23  import java.net.Socket;
24  import java.time.Clock;
25  import java.time.ZonedDateTime;
26  import java.time.format.DateTimeFormatter;
27  import java.util.Locale;
28  import java.util.Objects;
29  import java.util.concurrent.BlockingQueue;
30  import java.util.concurrent.LinkedBlockingQueue;
31  import java.util.concurrent.TimeUnit;
32  
33  import org.apache.commons.net.MockTcpServer;
34  
35  /**
36   * The MockDaytimeTCPServer class is a simple TCP implementation of a server for the Daytime Protocol described in
37   * <a href="https://datatracker.ietf.org/doc/html/rfc867">RFC 867</a>.
38   * <p>
39   * Listens for TCP socket connections on the daytime protocol port and writes the local day time to socket {@code outputStream} as {@link String} in format
40   * {@code EEEE, MMMM d, uuuu, HH:mm:ss-z}. See the <a href="https://datatracker.ietf.org/doc/html/rfc867"> RFC-867 spec </a> for more details.
41   * </p>
42   * <p>
43   * This implementation uses {@link MockDaytimeTCPServer#enqueue(Clock)} and {@link BlockingQueue<Clock>} to queue next {@link Clock} that will be used to obtain
44   * and write daytime data into {@code clientSocket}.
45   * </p>
46   * <p>
47   * NOTE: this is for <strong>debugging and testing purposes only</strong> and not meant to be run as a reliable server.
48   * </p>
49   *
50   * @see MockTcpServer
51   * @see DaytimeTCPClientTest DaytimeTCPClientTest (for example usage in tests)
52   */
53  public final class MockDaytimeTCPServer extends MockTcpServer {
54  
55      private static final DateTimeFormatter DAYTIME_DATA_FORMAT = DateTimeFormatter.ofPattern("EEEE, MMMM d, uuuu, HH:mm:ss-z", Locale.ENGLISH);
56  
57      private final BlockingQueue<Clock> responseQueue = new LinkedBlockingQueue<>();
58  
59      /**
60       * Creates new {@link MockDaytimeTCPServer} that will bind to {@link InetAddress#getLocalHost()}
61       * on random port.
62       *
63       * @throws IOException if an I/O error occurs when opening the socket.
64       */
65      public MockDaytimeTCPServer() throws IOException {
66      }
67  
68      /**
69       * Creates new {@link MockDaytimeTCPServer} that will bind to {@link InetAddress#getLocalHost()}
70       * on specified port.
71       *
72       * @param port the port number the server will bind to, or 0 to use a port number that is automatically allocated
73       * @throws IOException if an I/O error occurs when opening the socket.
74       */
75      public MockDaytimeTCPServer(final int port) throws IOException {
76          super(port);
77      }
78  
79      /**
80       * Creates new {@link MockDaytimeTCPServer} that will bind to specified {@link InetAddress} and on specified port.
81       *
82       * @param port the port number the server will bind to, or 0 to use a port number that is automatically allocated
83       * @param serverAddress the InetAddress the server will bind to
84       * @throws IOException if an I/O error occurs when opening the socket.
85       */
86      public MockDaytimeTCPServer(final int port, final InetAddress serverAddress) throws IOException {
87          super(port, serverAddress);
88      }
89  
90      /**
91       * Queues clock that will be used in next accepted {@link Socket}
92       * to return Daytime data, as defined in <a href="https://datatracker.ietf.org/doc/html/rfc867">RFC 867</a> spec
93       *
94       * @param clock that will be used
95       * @return the queued {@code clock}
96       */
97      public Clock enqueue(final Clock clock) {
98          responseQueue.add(clock);
99          return clock;
100     }
101 
102     @Override
103     protected void processClientSocket(final Socket clientSocket) throws Exception {
104         try (PrintWriter pw = new PrintWriter(clientSocket.getOutputStream())) {
105             final Clock nextClock = Objects.requireNonNull(responseQueue.poll(5, TimeUnit.SECONDS), "Could not obtain next clock for DaytimeTCPMockServer");
106             final ZonedDateTime dateTime = ZonedDateTime.now(nextClock);
107             pw.write(dateTime.format(DAYTIME_DATA_FORMAT));
108             pw.flush();
109         }
110     }
111 
112 }
113