    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.net;
    019
    020import java.net.DatagramSocket;
    021import java.net.InetAddress;
    022import java.net.SocketException;
    023import java.nio.charset.Charset;
    024
    025/**
    026 * The DatagramSocketClient provides the basic operations that are required of client objects accessing datagram sockets. It is meant to be subclassed to avoid
    027 * 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
    028 * {@link #setDatagramSocketFactory setDatagramSocketFactory } method, which allows you to control the type of DatagramSocket the DatagramSocketClient creates
    029 * for network communications. This is especially useful for adding things like proxy support as well as better support for applets. For example, you could
    030 * create a {@link org.apache.commons.net.DatagramSocketFactory} that requests browser security capabilities before creating a socket. All classes derived from
    031 * DatagramSocketClient should use the {@link #_socketFactory_ _socketFactory_ } member variable to create DatagramSocket instances rather than instantiating
    032 * 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
    033 * substituting his own SocketFactory.
    034 *
    035 *
    036 * @see DatagramSocketFactory
    037 */
    038
    039public abstract class DatagramSocketClient {
    040    /**
    041     * The default DatagramSocketFactory shared by all DatagramSocketClient instances.
    042     */
    043    private static final DatagramSocketFactory DEFAULT_SOCKET_FACTORY = new DefaultDatagramSocketFactory();
    044
    045    /**
    046     * Charset to use for byte IO.
    047     */
    048    private Charset charset = Charset.defaultCharset();
    049
    050    /** The timeout to use after opening a socket. */
    051    protected int _timeout_;
    052
    053    /** The datagram socket used for the connection. */
    054    protected DatagramSocket _socket_;
    055
    056    /**
    057     * A status variable indicating if the client's socket is currently open.
    058     */
    059    protected boolean _isOpen_;
    060
    061    /** The datagram socket's DatagramSocketFactory. */
    062    protected DatagramSocketFactory _socketFactory_;
    063
    064    /**
    065     * Default constructor for DatagramSocketClient. Initializes _socket_ to null, _timeout_ to 0, and _isOpen_ to false.
    066     */
    067    public DatagramSocketClient() {
    068        _socket_ = null;
    069        _timeout_ = 0;
    070        _isOpen_ = false;
    071        _socketFactory_ = DEFAULT_SOCKET_FACTORY;
    072    }
    073
    074    /**
    075     * 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
    076     * {@link #open open() } again. _isOpen_ is set to false and _socket_ is set to null.
    077     */
    078    public void close() {
    079        if (_socket_ != null) {
    080            _socket_.close();
    081        }
    082        _socket_ = null;
    083        _isOpen_ = false;
    084    }
    085
    086    /**
    087     * Gets the charset.
    088     *
    089     * @return the charset.
    090     * @since 3.3
    091     */
    092    public Charset getCharset() {
    093        return charset;
    094    }
    095
    096    /**
    097     * Gets the charset name.
    098     *
    099     * @return the charset name.
    100     * @since 3.3
    101     * @deprecated Use {@link #getCharset()} instead
    102     */
    103    @Deprecated
    104    public String getCharsetName() {
    105        return charset.name();
    106    }
    107
    108    /**
    109     * Returns the default timeout in milliseconds that is used when opening a socket.
    110     *
    111     * @return The default timeout in milliseconds that is used when opening a socket.
    112     */
    113    public int getDefaultTimeout() {
    114        return _timeout_;
    115    }
    116
    117    /**
    118     * Returns 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
    119     * thrown.
    120     *
    121     * @return The local address to which the client's socket is bound.
    122     */
    123    public InetAddress getLocalAddress() {
    124        return _socket_.getLocalAddress();
    125    }
    126
    127    /**
    128     * Returns 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
    129     * NullPointerException is thrown.
    130     *
    131     * @return The port number of the open socket on the local host used for the connection.
    132     */
    133    public int getLocalPort() {
    134        return _socket_.getLocalPort();
    135    }
    136
    137    /**
    138     * Returns the timeout in milliseconds of the currently opened socket. If you call this method when the client socket is not open, a NullPointerException is
    139     * thrown.
    140     *
    141     * @return The timeout in milliseconds of the currently opened socket.
    142     * @throws SocketException if an error getting the timeout
    143     */
    144    public int getSoTimeout() throws SocketException {
    145        return _socket_.getSoTimeout();
    146    }
    147
    148    /**
    149     * Returns true if the client has a currently open socket.
    150     *
    151     * @return True if the client has a currently open socket, false otherwise.
    152     */
    153    public boolean isOpen() {
    154        return _isOpen_;
    155    }
    156
    157    /**
    158     * 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
    159     * {@link #setDefaultTimeout setDefaultTimeout() }.
    160     * <p>
    161     * _isOpen_ is set to true after calling this method and _socket_ is set to the newly opened socket.
    162     *
    163     * @throws SocketException If the socket could not be opened or the timeout could not be set.
    164     */
    165    public void open() throws SocketException {
    166        _socket_ = _socketFactory_.createDatagramSocket();
    167        _socket_.setSoTimeout(_timeout_);
    168        _isOpen_ = true;
    169    }
    170
    171    /**
    172     * 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
    173     * setDefaultTimeout() }.
    174     * <p>
    175     * _isOpen_ is set to true after calling this method and _socket_ is set to the newly opened socket.
    176     *
    177     * @param port The port to use for the socket.
    178     * @throws SocketException If the socket could not be opened or the timeout could not be set.
    179     */
    180    public void open(final int port) throws SocketException {
    181        _socket_ = _socketFactory_.createDatagramSocket(port);
    182        _socket_.setSoTimeout(_timeout_);
    183        _isOpen_ = true;
    184    }
    185
    186    /**
    187     * 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
    188     * {@link #setDefaultTimeout setDefaultTimeout() }.
    189     * <p>
    190     * _isOpen_ is set to true after calling this method and _socket_ is set to the newly opened socket.
    191     *
    192     * @param port  The port to use for the socket.
    193     * @param laddr The local address to use.
    194     * @throws SocketException If the socket could not be opened or the timeout could not be set.
    195     */
    196    public void open(final int port, final InetAddress laddr) throws SocketException {
    197        _socket_ = _socketFactory_.createDatagramSocket(port, laddr);
    198        _socket_.setSoTimeout(_timeout_);
    199        _isOpen_ = true;
    200    }
    201
    202    /**
    203     * Sets the charset.
    204     *
    205     * @param charset the charset.
    206     * @since 3.3
    207     */
    208    public void setCharset(final Charset charset) {
    209        this.charset = charset;
    210    }
    211
    212    /**
    213     * Sets the DatagramSocketFactory used by the DatagramSocketClient to open DatagramSockets. If the factory value is null, then a default factory is used
    214     * (only do this to reset the factory after having previously altered it).
    215     *
    216     * @param factory The new DatagramSocketFactory the DatagramSocketClient should use.
    217     */
    218    public void setDatagramSocketFactory(final DatagramSocketFactory factory) {
    219        if (factory == null) {
    220            _socketFactory_ = DEFAULT_SOCKET_FACTORY;
    221        } else {
    222            _socketFactory_ = factory;
    223        }
    224    }
    225
    226    /**
    227     * Set 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
    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 in milliseconds to use for the datagram socket connection.
    232     */
    233    public void setDefaultTimeout(final int timeout) {
    234        _timeout_ = timeout;
    235    }
    236
    237    /**
    238     * Set the timeout in milliseconds of a currently open connection. Only call this method after a connection has been opened by {@link #open open()}.
    239     *
    240     * @param timeout The timeout in milliseconds to use for the currently open datagram socket connection.
    241     * @throws SocketException if an error setting the timeout
    242     */
    243    public void setSoTimeout(final int timeout) throws SocketException {
    244        _socket_.setSoTimeout(timeout);
    245    }
    246}