DatagramSocketClient.java

  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.  *      http://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. package org.apache.commons.net;

  18. import java.net.DatagramSocket;
  19. import java.net.InetAddress;
  20. import java.net.SocketException;
  21. import java.nio.charset.Charset;
  22. import java.time.Duration;
  23. import java.util.Objects;

  24. /**
  25.  * The DatagramSocketClient provides the basic operations that are required of client objects accessing datagram sockets. It is meant to be subclassed to avoid
  26.  * having to rewrite the same code over and over again to open a socket, close a socket, set timeouts, etc. Of special note is the
  27.  * {@link #setDatagramSocketFactory setDatagramSocketFactory } method, which allows you to control the type of DatagramSocket the DatagramSocketClient creates
  28.  * for network communications. This is especially useful for adding things like proxy support as well as better support for applets. For example, you could
  29.  * create a {@link org.apache.commons.net.DatagramSocketFactory} that requests browser security capabilities before creating a socket. All classes derived from
  30.  * DatagramSocketClient should use the {@link #_socketFactory_ _socketFactory_ } member variable to create DatagramSocket instances rather than instantiating
  31.  * them by directly invoking a constructor. By honoring this contract you guarantee that a user will always be able to provide his own Socket implementations by
  32.  * substituting his own SocketFactory.
  33.  *
  34.  *
  35.  * @see DatagramSocketFactory
  36.  */

  37. public abstract class DatagramSocketClient implements AutoCloseable {
  38.     /**
  39.      * The default DatagramSocketFactory shared by all DatagramSocketClient instances.
  40.      */
  41.     private static final DatagramSocketFactory DEFAULT_SOCKET_FACTORY = new DefaultDatagramSocketFactory();

  42.     /**
  43.      * Charset to use for byte IO.
  44.      */
  45.     private Charset charset = Charset.defaultCharset();

  46.     /** The timeout to use after opening a socket. */
  47.     protected int _timeout_;

  48.     /** The datagram socket used for the connection. */
  49.     protected DatagramSocket _socket_;

  50.     /**
  51.      * A status variable indicating if the client's socket is currently open.
  52.      */
  53.     protected boolean _isOpen_;

  54.     /** The datagram socket's DatagramSocketFactory. */
  55.     protected DatagramSocketFactory _socketFactory_ = DEFAULT_SOCKET_FACTORY;

  56.     /**
  57.      * Default constructor for DatagramSocketClient. Initializes _socket_ to null, _timeout_ to 0, and _isOpen_ to false.
  58.      */
  59.     public DatagramSocketClient() {
  60.     }

  61.     /**
  62.      * Gets the non-null DatagramSocket or throws {@link NullPointerException}.
  63.      *
  64.      * <p>
  65.      * This method does not allocate resources.
  66.      * </p>
  67.      *
  68.      * @return the non-null DatagramSocket.
  69.      * @since 3.10.0
  70.      */
  71.     protected DatagramSocket checkOpen() {
  72.         return Objects.requireNonNull(_socket_, "DatagramSocket");
  73.     }

  74.     /**
  75.      * Closes the DatagramSocket used for the connection. You should call this method after you've finished using the class instance and also before you call
  76.      * {@link #open open() } again. _isOpen_ is set to false and _socket_ is set to null.
  77.      */
  78.     @Override
  79.     public void close() {
  80.         if (_socket_ != null) {
  81.             _socket_.close();
  82.         }
  83.         _socket_ = null;
  84.         _isOpen_ = false;
  85.     }

  86.     /**
  87.      * Gets the charset.
  88.      *
  89.      * @return the charset.
  90.      * @since 3.3
  91.      */
  92.     public Charset getCharset() {
  93.         return charset;
  94.     }

  95.     /**
  96.      * Gets the charset name.
  97.      *
  98.      * @return the charset name.
  99.      * @since 3.3
  100.      * @deprecated Use {@link #getCharset()} instead
  101.      */
  102.     @Deprecated
  103.     public String getCharsetName() {
  104.         return charset.name();
  105.     }

  106.     /**
  107.      * Gets the default timeout in milliseconds that is used when opening a socket.
  108.      *
  109.      * @return The default timeout in milliseconds that is used when opening a socket.
  110.      */
  111.     public int getDefaultTimeout() {
  112.         return _timeout_;
  113.     }

  114.     /**
  115.      * Gets the local address to which the client's socket is bound. If you call this method when the client socket is not open, a NullPointerException is
  116.      * thrown.
  117.      *
  118.      * @return The local address to which the client's socket is bound.
  119.      */
  120.     public InetAddress getLocalAddress() {
  121.         return checkOpen().getLocalAddress();
  122.     }

  123.     /**
  124.      * Gets the port number of the open socket on the local host used for the connection. If you call this method when the client socket is not open, a
  125.      * NullPointerException is thrown.
  126.      *
  127.      * @return The port number of the open socket on the local host used for the connection.
  128.      */
  129.     public int getLocalPort() {
  130.         return checkOpen().getLocalPort();
  131.     }

  132.     /**
  133.      * Gets the timeout in milliseconds of the currently opened socket. If you call this method when the client socket is not open, a NullPointerException is
  134.      * thrown.
  135.      *
  136.      * @return The timeout in milliseconds of the currently opened socket.
  137.      * @throws SocketException if an error getting the timeout.
  138.      * @deprecated Use {@link #getSoTimeoutDuration()}.
  139.      */
  140.     @Deprecated
  141.     public int getSoTimeout() throws SocketException {
  142.         return checkOpen().getSoTimeout();
  143.     }

  144.     /**
  145.      * Gets the timeout duration of the currently opened socket. If you call this method when the client socket is not open, a NullPointerException is
  146.      * thrown.
  147.      *
  148.      * @return The timeout in milliseconds of the currently opened socket.
  149.      * @throws SocketException if an error getting the timeout.
  150.      */
  151.     public Duration getSoTimeoutDuration() throws SocketException {
  152.         return Duration.ofMillis(checkOpen().getSoTimeout());
  153.     }

  154.     /**
  155.      * Gets true if the client has a currently open socket.
  156.      *
  157.      * @return True if the client has a currently open socket, false otherwise.
  158.      */
  159.     public boolean isOpen() {
  160.         return _isOpen_;
  161.     }

  162.     /**
  163.      * Opens a DatagramSocket on the local host at the first available port. Also sets the timeout on the socket to the default timeout set by
  164.      * {@link #setDefaultTimeout setDefaultTimeout() }.
  165.      * <p>
  166.      * _isOpen_ is set to true after calling this method and _socket_ is set to the newly opened socket.
  167.      *
  168.      * @throws SocketException If the socket could not be opened or the timeout could not be set.
  169.      */
  170.     public void open() throws SocketException {
  171.         _socket_ = _socketFactory_.createDatagramSocket();
  172.         _socket_.setSoTimeout(_timeout_);
  173.         _isOpen_ = true;
  174.     }

  175.     /**
  176.      * Opens a DatagramSocket on the local host at a specified port. Also sets the timeout on the socket to the default timeout set by {@link #setDefaultTimeout
  177.      * setDefaultTimeout() }.
  178.      * <p>
  179.      * _isOpen_ is set to true after calling this method and _socket_ is set to the newly opened socket.
  180.      *
  181.      * @param port The port to use for the socket.
  182.      * @throws SocketException If the socket could not be opened or the timeout could not be set.
  183.      */
  184.     public void open(final int port) throws SocketException {
  185.         _socket_ = _socketFactory_.createDatagramSocket(port);
  186.         _socket_.setSoTimeout(_timeout_);
  187.         _isOpen_ = true;
  188.     }

  189.     /**
  190.      * Opens a DatagramSocket at the specified address on the local host at a specified port. Also sets the timeout on the socket to the default timeout set by
  191.      * {@link #setDefaultTimeout setDefaultTimeout() }.
  192.      * <p>
  193.      * _isOpen_ is set to true after calling this method and _socket_ is set to the newly opened socket.
  194.      *
  195.      * @param port  The port to use for the socket.
  196.      * @param localAddress The local address to use.
  197.      * @throws SocketException If the socket could not be opened or the timeout could not be set.
  198.      */
  199.     public void open(final int port, final InetAddress localAddress) throws SocketException {
  200.         _socket_ = _socketFactory_.createDatagramSocket(port, localAddress);
  201.         _socket_.setSoTimeout(_timeout_);
  202.         _isOpen_ = true;
  203.     }

  204.     /**
  205.      * Sets the charset.
  206.      *
  207.      * @param charset the charset.
  208.      * @since 3.3
  209.      */
  210.     public void setCharset(final Charset charset) {
  211.         this.charset = charset;
  212.     }

  213.     /**
  214.      * Sets the DatagramSocketFactory used by the DatagramSocketClient to open DatagramSockets. If the factory value is null, then a default factory is used
  215.      * (only do this to reset the factory after having previously altered it).
  216.      *
  217.      * @param factory The new DatagramSocketFactory the DatagramSocketClient should use.
  218.      */
  219.     public void setDatagramSocketFactory(final DatagramSocketFactory factory) {
  220.         if (factory == null) {
  221.             _socketFactory_ = DEFAULT_SOCKET_FACTORY;
  222.         } else {
  223.             _socketFactory_ = factory;
  224.         }
  225.     }

  226.     /**
  227.      * Sets the default timeout in to use when opening a socket. After a call to open, the timeout for the socket is set using this value. This
  228.      * method should be used prior to a call to {@link #open open()} and should not be confused with {@link #setSoTimeout setSoTimeout()} which operates on the
  229.      * currently open socket. _timeout_ contains the new timeout value.
  230.      *
  231.      * @param timeout The timeout durations to use for the datagram socket connection.
  232.      */
  233.     public void setDefaultTimeout(final Duration timeout) {
  234.         _timeout_ = Math.toIntExact(timeout.toMillis());
  235.     }

  236.     /**
  237.      * Sets the default timeout in milliseconds to use when opening a socket. After a call to open, the timeout for the socket is set using this value. This
  238.      * method should be used prior to a call to {@link #open open()} and should not be confused with {@link #setSoTimeout setSoTimeout()} which operates on the
  239.      * currently open socket. _timeout_ contains the new timeout value.
  240.      *
  241.      * @param timeout The timeout in milliseconds to use for the datagram socket connection.
  242.      * @deprecated Use {@link #setDefaultTimeout(Duration)}.
  243.      */
  244.     @Deprecated
  245.     public void setDefaultTimeout(final int timeout) {
  246.         _timeout_ = timeout;
  247.     }

  248.     /**
  249.      * Sets the timeout duration of a currently open connection. Only call this method after a connection has been opened by {@link #open open()}.
  250.      *
  251.      * @param timeout The timeout in milliseconds to use for the currently open datagram socket connection.
  252.      * @throws SocketException if an error setting the timeout.
  253.      * @since 3.10.0
  254.      */
  255.     public void setSoTimeout(final Duration timeout) throws SocketException {
  256.         checkOpen().setSoTimeout(Math.toIntExact(timeout.toMillis()));
  257.     }

  258.     /**
  259.      * Sets the timeout in milliseconds of a currently open connection. Only call this method after a connection has been opened by {@link #open open()}.
  260.      *
  261.      * @param timeout The timeout in milliseconds to use for the currently open datagram socket connection.
  262.      * @throws SocketException if an error setting the timeout.
  263.      * @deprecated Use {@link #setSoTimeout(Duration)}.
  264.      */
  265.     @Deprecated
  266.     public void setSoTimeout(final int timeout) throws SocketException {
  267.         checkOpen().setSoTimeout(timeout);
  268.     }
  269. }