    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.io.Closeable;
    021import java.io.IOException;
    022import java.io.InputStream;
    023import java.io.OutputStream;
    024import java.net.InetAddress;
    025import java.net.InetSocketAddress;
    026import java.net.Proxy;
    027import java.net.Socket;
    028import java.net.SocketException;
    029import java.nio.charset.Charset;
    030
    031import javax.net.ServerSocketFactory;
    032import javax.net.SocketFactory;
    033
    034
    035/**
    036 * The SocketClient provides the basic operations that are required of
    037 * client objects accessing sockets.  It is meant to be
    038 * subclassed to avoid having to rewrite the same code over and over again
    039 * to open a socket, close a socket, set timeouts, etc.  Of special note
    040 * is the {@link #setSocketFactory  setSocketFactory }
    041 * method, which allows you to control the type of Socket the SocketClient
    042 * creates for initiating network connections.  This is especially useful
    043 * for adding SSL or proxy support as well as better support for applets.  For
    044 * example, you could create a
    045 * {@link javax.net.SocketFactory} that
    046 * requests browser security capabilities before creating a socket.
    047 * All classes derived from SocketClient should use the
    048 * {@link #_socketFactory_  _socketFactory_ } member variable to
    049 * create Socket and ServerSocket instances rather than instantiating
    050 * them by directly invoking a constructor.  By honoring this contract
    051 * you guarantee that a user will always be able to provide his own
    052 * Socket implementations by substituting his own SocketFactory.
    053 * @see SocketFactory
    054 */
    055public abstract class SocketClient
    056{
    057    /**
    058     * The end of line character sequence used by most IETF protocols.  That
    059     * is a carriage return followed by a newline: "\r\n"
    060     */
    061    public static final String NETASCII_EOL = "\r\n";
    062
    063    /** The default SocketFactory shared by all SocketClient instances. */
    064    private static final SocketFactory __DEFAULT_SOCKET_FACTORY =
    065            SocketFactory.getDefault();
    066
    067    /** The default {@link ServerSocketFactory} */
    068    private static final ServerSocketFactory __DEFAULT_SERVER_SOCKET_FACTORY =
    069            ServerSocketFactory.getDefault();
    070
    071    /**
    072     * A ProtocolCommandSupport object used to manage the registering of
    073     * ProtocolCommandListeners and the firing of ProtocolCommandEvents.
    074     */
    075    private ProtocolCommandSupport __commandSupport;
    076
    077    /** The timeout to use after opening a socket. */
    078    protected int _timeout_;
    079
    080    /** The socket used for the connection. */
    081    protected Socket _socket_;
    082
    083    /** The hostname used for the connection (null = no hostname supplied). */
    084    protected String _hostname_;
    085
    086    /** The default port the client should connect to. */
    087    protected int _defaultPort_;
    088
    089    /** The socket's InputStream. */
    090    protected InputStream _input_;
    091
    092    /** The socket's OutputStream. */
    093    protected OutputStream _output_;
    094
    095    /** The socket's SocketFactory. */
    096    protected SocketFactory _socketFactory_;
    097
    098    /** The socket's ServerSocket Factory. */
    099    protected ServerSocketFactory _serverSocketFactory_;
    100
    101    /** The socket's connect timeout (0 = infinite timeout) */
    102    private static final int DEFAULT_CONNECT_TIMEOUT = 0;
    103    protected int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
    104
    105    /** Hint for SO_RCVBUF size */
    106    private int receiveBufferSize = -1;
    107
    108    /** Hint for SO_SNDBUF size */
    109    private int sendBufferSize = -1;
    110
    111    /** The proxy to use when connecting. */
    112    private Proxy connProxy;
    113
    114    /**
    115     * Charset to use for byte IO.
    116     */
    117    private Charset charset = Charset.defaultCharset();
    118
    119    /**
    120     * Default constructor for SocketClient.  Initializes
    121     * _socket_ to null, _timeout_ to 0, _defaultPort to 0,
    122     * _isConnected_ to false, charset to {@code Charset.defaultCharset()}
    123     * and _socketFactory_ to a shared instance of
    124     * {@link org.apache.commons.net.DefaultSocketFactory}.
    125     */
    126    public SocketClient()
    127    {
    128        _socket_ = null;
    129        _hostname_ = null;
    130        _input_ = null;
    131        _output_ = null;
    132        _timeout_ = 0;
    133        _defaultPort_ = 0;
    134        _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
    135        _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY;
    136    }
    137
    138
    139    /**
    140     * Because there are so many connect() methods, the _connectAction_()
    141     * method is provided as a means of performing some action immediately
    142     * after establishing a connection, rather than reimplementing all
    143     * of the connect() methods.  The last action performed by every
    144     * connect() method after opening a socket is to call this method.
    145     * <p>
    146     * This method sets the timeout on the just opened socket to the default
    147     * timeout set by {@link #setDefaultTimeout  setDefaultTimeout() },
    148     * sets _input_ and _output_ to the socket's InputStream and OutputStream
    149     * respectively, and sets _isConnected_ to true.
    150     * <p>
    151     * Subclasses overriding this method should start by calling
    152     * <code> super._connectAction_() </code> first to ensure the
    153     * initialization of the aforementioned protected variables.
    154     * @throws IOException (SocketException) if a problem occurs with the socket
    155     */
    156    protected void _connectAction_() throws IOException
    157    {
    158        _socket_.setSoTimeout(_timeout_);
    159        _input_ = _socket_.getInputStream();
    160        _output_ = _socket_.getOutputStream();
    161    }
    162
    163
    164    /**
    165     * Opens a Socket connected to a remote host at the specified port and
    166     * originating from the current host at a system assigned port.
    167     * Before returning, {@link #_connectAction_  _connectAction_() }
    168     * is called to perform connection initialization actions.
    169     * <p>
    170     * @param host  The remote host.
    171     * @param port  The port to connect to on the remote host.
    172     * @throws SocketException If the socket timeout could not be set.
    173     * @throws IOException If the socket could not be opened.  In most
    174     *  cases you will only want to catch IOException since SocketException is
    175     *  derived from it.
    176     */
    177    public void connect(InetAddress host, int port)
    178    throws SocketException, IOException
    179    {
    180        _hostname_ = null;
    181        _connect(host, port, null, -1);
    182    }
    183
    184    /**
    185     * Opens a Socket connected to a remote host at the specified port and
    186     * originating from the current host at a system assigned port.
    187     * Before returning, {@link #_connectAction_  _connectAction_() }
    188     * is called to perform connection initialization actions.
    189     * <p>
    190     * @param hostname  The name of the remote host.
    191     * @param port  The port to connect to on the remote host.
    192     * @throws SocketException If the socket timeout could not be set.
    193     * @throws IOException If the socket could not be opened.  In most
    194     *  cases you will only want to catch IOException since SocketException is
    195     *  derived from it.
    196     * @throws java.net.UnknownHostException If the hostname cannot be resolved.
    197     */
    198    public void connect(String hostname, int port)
    199    throws SocketException, IOException
    200    {
    201        _hostname_ = hostname;
    202        _connect(InetAddress.getByName(hostname), port, null, -1);
    203    }
    204
    205
    206    /**
    207     * Opens a Socket connected to a remote host at the specified port and
    208     * originating from the specified local address and port.
    209     * Before returning, {@link #_connectAction_  _connectAction_() }
    210     * is called to perform connection initialization actions.
    211     * <p>
    212     * @param host  The remote host.
    213     * @param port  The port to connect to on the remote host.
    214     * @param localAddr  The local address to use.
    215     * @param localPort  The local port to use.
    216     * @throws SocketException If the socket timeout could not be set.
    217     * @throws IOException If the socket could not be opened.  In most
    218     *  cases you will only want to catch IOException since SocketException is
    219     *  derived from it.
    220     */
    221    public void connect(InetAddress host, int port,
    222                        InetAddress localAddr, int localPort)
    223    throws SocketException, IOException
    224    {
    225        _hostname_ = null;
    226        _connect(host, port, localAddr, localPort);
    227    }
    228
    229    // helper method to allow code to be shared with connect(String,...) methods
    230    private void _connect(InetAddress host, int port, InetAddress localAddr, int localPort)
    231        throws SocketException, IOException
    232    {
    233        _socket_ = _socketFactory_.createSocket();
    234        if (receiveBufferSize != -1) {
    235            _socket_.setReceiveBufferSize(receiveBufferSize);
    236        }
    237        if (sendBufferSize != -1) {
    238            _socket_.setSendBufferSize(sendBufferSize);
    239        }
    240        if (localAddr != null) {
    241            _socket_.bind(new InetSocketAddress(localAddr, localPort));
    242        }
    243        _socket_.connect(new InetSocketAddress(host, port), connectTimeout);
    244        _connectAction_();
    245    }
    246
    247    /**
    248     * Opens a Socket connected to a remote host at the specified port and
    249     * originating from the specified local address and port.
    250     * Before returning, {@link #_connectAction_  _connectAction_() }
    251     * is called to perform connection initialization actions.
    252     * <p>
    253     * @param hostname  The name of the remote host.
    254     * @param port  The port to connect to on the remote host.
    255     * @param localAddr  The local address to use.
    256     * @param localPort  The local port to use.
    257     * @throws SocketException If the socket timeout could not be set.
    258     * @throws IOException If the socket could not be opened.  In most
    259     *  cases you will only want to catch IOException since SocketException is
    260     *  derived from it.
    261     * @throws java.net.UnknownHostException If the hostname cannot be resolved.
    262     */
    263    public void connect(String hostname, int port,
    264                        InetAddress localAddr, int localPort)
    265    throws SocketException, IOException
    266    {
    267        _hostname_ = hostname;
    268       _connect(InetAddress.getByName(hostname), port, localAddr, localPort);
    269    }
    270
    271
    272    /**
    273     * Opens a Socket connected to a remote host at the current default port
    274     * and originating from the current host at a system assigned port.
    275     * Before returning, {@link #_connectAction_  _connectAction_() }
    276     * is called to perform connection initialization actions.
    277     * <p>
    278     * @param host  The remote host.
    279     * @throws SocketException If the socket timeout could not be set.
    280     * @throws IOException If the socket could not be opened.  In most
    281     *  cases you will only want to catch IOException since SocketException is
    282     *  derived from it.
    283     */
    284    public void connect(InetAddress host) throws SocketException, IOException
    285    {
    286        _hostname_ = null;
    287        connect(host, _defaultPort_);
    288    }
    289
    290
    291    /**
    292     * Opens a Socket connected to a remote host at the current default
    293     * port and originating from the current host at a system assigned port.
    294     * Before returning, {@link #_connectAction_  _connectAction_() }
    295     * is called to perform connection initialization actions.
    296     * <p>
    297     * @param hostname  The name of the remote host.
    298     * @throws SocketException If the socket timeout could not be set.
    299     * @throws IOException If the socket could not be opened.  In most
    300     *  cases you will only want to catch IOException since SocketException is
    301     *  derived from it.
    302     * @throws java.net.UnknownHostException If the hostname cannot be resolved.
    303     */
    304    public void connect(String hostname) throws SocketException, IOException
    305    {
    306        connect(hostname, _defaultPort_);
    307    }
    308
    309
    310    /**
    311     * Disconnects the socket connection.
    312     * You should call this method after you've finished using the class
    313     * instance and also before you call
    314     * {@link #connect connect() }
    315     * again.  _isConnected_ is set to false, _socket_ is set to null,
    316     * _input_ is set to null, and _output_ is set to null.
    317     * <p>
    318     * @throws IOException  If there is an error closing the socket.
    319     */
    320    public void disconnect() throws IOException
    321    {
    322        closeQuietly(_socket_);
    323        closeQuietly(_input_);
    324        closeQuietly(_output_);
    325        _socket_ = null;
    326        _hostname_ = null;
    327        _input_ = null;
    328        _output_ = null;
    329    }
    330
    331    private void closeQuietly(Socket socket) {
    332        if (socket != null){
    333            try {
    334                socket.close();
    335            } catch (IOException e) {
    336                // Ignored
    337            }
    338        }
    339    }
    340
    341    private void closeQuietly(Closeable close){
    342        if (close != null){
    343            try {
    344                close.close();
    345            } catch (IOException e) {
    346                // Ignored
    347            }
    348        }
    349    }
    350    /**
    351     * Returns true if the client is currently connected to a server.
    352     * <p>
    353     * Delegates to {@link Socket#isConnected()}
    354     * @return True if the client is currently connected to a server,
    355     *         false otherwise.
    356     */
    357    public boolean isConnected()
    358    {
    359        if (_socket_ == null) {
    360            return false;
    361        }
    362
    363        return _socket_.isConnected();
    364    }
    365
    366    /**
    367     * Make various checks on the socket to test if it is available for use.
    368     * Note that the only sure test is to use it, but these checks may help
    369     * in some cases.
    370     * @see <a href="https://issues.apache.org/jira/browse/NET-350">NET-350</a>
    371     * @return {@code true} if the socket appears to be available for use
    372     * @since 3.0
    373     */
    374    public boolean isAvailable(){
    375        if (isConnected()) {
    376            try
    377            {
    378                if (_socket_.getInetAddress() == null) {
    379                    return false;
    380                }
    381                if (_socket_.getPort() == 0) {
    382                    return false;
    383                }
    384                if (_socket_.getRemoteSocketAddress() == null) {
    385                    return false;
    386                }
    387                if (_socket_.isClosed()) {
    388                    return false;
    389                }
    390                /* these aren't exact checks (a Socket can be half-open),
    391                   but since we usually require two-way data transfer,
    392                   we check these here too: */
    393                if (_socket_.isInputShutdown()) {
    394                    return false;
    395                }
    396                if (_socket_.isOutputShutdown()) {
    397                    return false;
    398                }
    399                /* ignore the result, catch exceptions: */
    400                _socket_.getInputStream();
    401                _socket_.getOutputStream();
    402            }
    403            catch (IOException ioex)
    404            {
    405                return false;
    406            }
    407            return true;
    408        } else {
    409            return false;
    410        }
    411    }
    412
    413    /**
    414     * Sets the default port the SocketClient should connect to when a port
    415     * is not specified.  The {@link #_defaultPort_  _defaultPort_ }
    416     * variable stores this value.  If never set, the default port is equal
    417     * to zero.
    418     * <p>
    419     * @param port  The default port to set.
    420     */
    421    public void setDefaultPort(int port)
    422    {
    423        _defaultPort_ = port;
    424    }
    425
    426    /**
    427     * Returns the current value of the default port (stored in
    428     * {@link #_defaultPort_  _defaultPort_ }).
    429     * <p>
    430     * @return The current value of the default port.
    431     */
    432    public int getDefaultPort()
    433    {
    434        return _defaultPort_;
    435    }
    436
    437
    438    /**
    439     * Set the default timeout in milliseconds to use when opening a socket.
    440     * This value is only used previous to a call to
    441     * {@link #connect connect()}
    442     * and should not be confused with {@link #setSoTimeout setSoTimeout()}
    443     * which operates on an the currently opened socket.  _timeout_ contains
    444     * the new timeout value.
    445     * <p>
    446     * @param timeout  The timeout in milliseconds to use for the socket
    447     *                 connection.
    448     */
    449    public void setDefaultTimeout(int timeout)
    450    {
    451        _timeout_ = timeout;
    452    }
    453
    454
    455    /**
    456     * Returns the default timeout in milliseconds that is used when
    457     * opening a socket.
    458     * <p>
    459     * @return The default timeout in milliseconds that is used when
    460     *         opening a socket.
    461     */
    462    public int getDefaultTimeout()
    463    {
    464        return _timeout_;
    465    }
    466
    467
    468    /**
    469     * Set the timeout in milliseconds of a currently open connection.
    470     * Only call this method after a connection has been opened
    471     * by {@link #connect connect()}.
    472     * <p>
    473     * To set the initial timeout, use {@link #setDefaultTimeout(int)} instead.
    474     *
    475     * @param timeout  The timeout in milliseconds to use for the currently
    476     *                 open socket connection.
    477     * @throws SocketException If the operation fails.
    478     * @throws NullPointerException if the socket is not currently open
    479     */
    480    public void setSoTimeout(int timeout) throws SocketException
    481    {
    482        _socket_.setSoTimeout(timeout);
    483    }
    484
    485
    486    /**
    487     * Set the underlying socket send buffer size.
    488     * <p>
    489     * @param size The size of the buffer in bytes.
    490     * @throws SocketException never thrown, but subclasses might want to do so
    491     * @since 2.0
    492     */
    493    public void setSendBufferSize(int size) throws SocketException {
    494        sendBufferSize = size;
    495    }
    496
    497    /**
    498     * Get the current sendBuffer size
    499     * @return the size, or -1 if not initialised
    500     * @since 3.0
    501     */
    502    protected int getSendBufferSize(){
    503        return sendBufferSize;
    504    }
    505
    506    /**
    507     * Sets the underlying socket receive buffer size.
    508     * <p>
    509     * @param size The size of the buffer in bytes.
    510     * @throws SocketException never (but subclasses may wish to do so)
    511     * @since 2.0
    512     */
    513    public void setReceiveBufferSize(int size) throws SocketException  {
    514        receiveBufferSize = size;
    515    }
    516
    517    /**
    518     * Get the current receivedBuffer size
    519     * @return the size, or -1 if not initialised
    520     * @since 3.0
    521     */
    522    protected int getReceiveBufferSize(){
    523        return receiveBufferSize;
    524    }
    525
    526    /**
    527     * Returns the timeout in milliseconds of the currently opened socket.
    528     * <p>
    529     * @return The timeout in milliseconds of the currently opened socket.
    530     * @throws SocketException If the operation fails.
    531     * @throws NullPointerException if the socket is not currently open
    532     */
    533    public int getSoTimeout() throws SocketException
    534    {
    535        return _socket_.getSoTimeout();
    536    }
    537
    538    /**
    539     * Enables or disables the Nagle's algorithm (TCP_NODELAY) on the
    540     * currently opened socket.
    541     * <p>
    542     * @param on  True if Nagle's algorithm is to be enabled, false if not.
    543     * @throws SocketException If the operation fails.
    544     * @throws NullPointerException if the socket is not currently open
    545     */
    546    public void setTcpNoDelay(boolean on) throws SocketException
    547    {
    548        _socket_.setTcpNoDelay(on);
    549    }
    550
    551
    552    /**
    553     * Returns true if Nagle's algorithm is enabled on the currently opened
    554     * socket.
    555     * <p>
    556     * @return True if Nagle's algorithm is enabled on the currently opened
    557     *        socket, false otherwise.
    558     * @throws SocketException If the operation fails.
    559     * @throws NullPointerException if the socket is not currently open
    560     */
    561    public boolean getTcpNoDelay() throws SocketException
    562    {
    563        return _socket_.getTcpNoDelay();
    564    }
    565
    566    /**
    567     * Sets the SO_KEEPALIVE flag on the currently opened socket.
    568     *
    569     * From the Javadocs, the default keepalive time is 2 hours (although this is
    570     * implementation  dependent). It looks as though the Windows WSA sockets implementation
    571     * allows a specific keepalive value to be set, although this seems not to be the case on
    572     * other systems.
    573     * @param  keepAlive If true, keepAlive is turned on
    574     * @throws SocketException if there is a problem with the socket
    575     * @throws NullPointerException if the socket is not currently open
    576     * @since 2.2
    577     */
    578    public void setKeepAlive(boolean keepAlive) throws SocketException {
    579        _socket_.setKeepAlive(keepAlive);
    580    }
    581
    582    /**
    583     * Returns the current value of the SO_KEEPALIVE flag on the currently opened socket.
    584     * Delegates to {@link Socket#getKeepAlive()}
    585     * @return True if SO_KEEPALIVE is enabled.
    586     * @throws SocketException if there is a problem with the socket
    587     * @throws NullPointerException if the socket is not currently open
    588     * @since 2.2
    589     */
    590    public boolean getKeepAlive() throws SocketException {
    591        return _socket_.getKeepAlive();
    592    }
    593
    594    /**
    595     * Sets the SO_LINGER timeout on the currently opened socket.
    596     * <p>
    597     * @param on  True if linger is to be enabled, false if not.
    598     * @param val The linger timeout (in hundredths of a second?)
    599     * @throws SocketException If the operation fails.
    600     * @throws NullPointerException if the socket is not currently open
    601     */
    602    public void setSoLinger(boolean on, int val) throws SocketException
    603    {
    604        _socket_.setSoLinger(on, val);
    605    }
    606
    607
    608    /**
    609     * Returns the current SO_LINGER timeout of the currently opened socket.
    610     * <p>
    611     * @return The current SO_LINGER timeout.  If SO_LINGER is disabled returns
    612     *         -1.
    613     * @throws SocketException If the operation fails.
    614     * @throws NullPointerException if the socket is not currently open
    615     */
    616    public int getSoLinger() throws SocketException
    617    {
    618        return _socket_.getSoLinger();
    619    }
    620
    621
    622    /**
    623     * Returns the port number of the open socket on the local host used
    624     * for the connection.
    625     * Delegates to {@link Socket#getLocalPort()}
    626     * <p>
    627     * @return The port number of the open socket on the local host used
    628     *         for the connection.
    629     * @throws NullPointerException if the socket is not currently open
    630     */
    631    public int getLocalPort()
    632    {
    633        return _socket_.getLocalPort();
    634    }
    635
    636
    637    /**
    638     * Returns the local address  to which the client's socket is bound.
    639     * Delegates to {@link Socket#getLocalAddress()}
    640     * <p>
    641     * @return The local address to which the client's socket is bound.
    642     * @throws NullPointerException if the socket is not currently open
    643     */
    644    public InetAddress getLocalAddress()
    645    {
    646        return _socket_.getLocalAddress();
    647    }
    648
    649    /**
    650     * Returns the port number of the remote host to which the client is
    651     * connected.
    652     * Delegates to {@link Socket#getPort()}
    653     * <p>
    654     * @return The port number of the remote host to which the client is
    655     *         connected.
    656     * @throws NullPointerException if the socket is not currently open
    657     */
    658    public int getRemotePort()
    659    {
    660        return _socket_.getPort();
    661    }
    662
    663
    664    /**
    665     * @return The remote address to which the client is connected.
    666     * Delegates to {@link Socket#getInetAddress()}
    667     * @throws NullPointerException if the socket is not currently open
    668     */
    669    public InetAddress getRemoteAddress()
    670    {
    671        return _socket_.getInetAddress();
    672    }
    673
    674
    675    /**
    676     * Verifies that the remote end of the given socket is connected to the
    677     * the same host that the SocketClient is currently connected to.  This
    678     * is useful for doing a quick security check when a client needs to
    679     * accept a connection from a server, such as an FTP data connection or
    680     * a BSD R command standard error stream.
    681     * <p>
    682     * @param socket the item to check against
    683     * @return True if the remote hosts are the same, false if not.
    684     */
    685    public boolean verifyRemote(Socket socket)
    686    {
    687        InetAddress host1, host2;
    688
    689        host1 = socket.getInetAddress();
    690        host2 = getRemoteAddress();
    691
    692        return host1.equals(host2);
    693    }
    694
    695
    696    /**
    697     * Sets the SocketFactory used by the SocketClient to open socket
    698     * connections.  If the factory value is null, then a default
    699     * factory is used (only do this to reset the factory after having
    700     * previously altered it).
    701     * Any proxy setting is discarded.
    702     * <p>
    703     * @param factory  The new SocketFactory the SocketClient should use.
    704     */
    705    public void setSocketFactory(SocketFactory factory)
    706    {
    707        if (factory == null) {
    708            _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
    709        } else {
    710            _socketFactory_ = factory;
    711        }
    712        // re-setting the socket factory makes the proxy setting useless,
    713        // so set the field to null so that getProxy() doesn't return a
    714        // Proxy that we're actually not using.
    715        connProxy = null;
    716    }
    717
    718    /**
    719     * Sets the ServerSocketFactory used by the SocketClient to open ServerSocket
    720     * connections.  If the factory value is null, then a default
    721     * factory is used (only do this to reset the factory after having
    722     * previously altered it).
    723     * <p>
    724     * @param factory  The new ServerSocketFactory the SocketClient should use.
    725     * @since 2.0
    726     */
    727    public void setServerSocketFactory(ServerSocketFactory factory) {
    728        if (factory == null) {
    729            _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY;
    730        } else {
    731            _serverSocketFactory_ = factory;
    732        }
    733    }
    734
    735    /**
    736     * Sets the connection timeout in milliseconds, which will be passed to the {@link Socket} object's
    737     * connect() method.
    738     * @param connectTimeout The connection timeout to use (in ms)
    739     * @since 2.0
    740     */
    741    public void setConnectTimeout(int connectTimeout) {
    742        this.connectTimeout = connectTimeout;
    743    }
    744
    745    /**
    746     * Get the underlying socket connection timeout.
    747     * @return timeout (in ms)
    748     * @since 2.0
    749     */
    750    public int getConnectTimeout() {
    751        return connectTimeout;
    752    }
    753
    754    /**
    755     * Get the underlying {@link ServerSocketFactory}
    756     * @return The server socket factory
    757     * @since 2.2
    758     */
    759    public ServerSocketFactory getServerSocketFactory() {
    760        return _serverSocketFactory_;
    761    }
    762
    763
    764    /**
    765     * Adds a ProtocolCommandListener.
    766     *
    767     * @param listener  The ProtocolCommandListener to add.
    768     * @since 3.0
    769     */
    770    public void addProtocolCommandListener(ProtocolCommandListener listener) {
    771        getCommandSupport().addProtocolCommandListener(listener);
    772    }
    773
    774    /**
    775     * Removes a ProtocolCommandListener.
    776     *
    777     * @param listener  The ProtocolCommandListener to remove.
    778     * @since 3.0
    779     */
    780    public void removeProtocolCommandListener(ProtocolCommandListener listener) {
    781        getCommandSupport().removeProtocolCommandListener(listener);
    782    }
    783
    784    /**
    785     * If there are any listeners, send them the reply details.
    786     *
    787     * @param replyCode the code extracted from the reply
    788     * @param reply the full reply text
    789     * @since 3.0
    790     */
    791    protected void fireReplyReceived(int replyCode, String reply) {
    792        if (getCommandSupport().getListenerCount() > 0) {
    793            getCommandSupport().fireReplyReceived(replyCode, reply);
    794        }
    795    }
    796
    797    /**
    798     * If there are any listeners, send them the command details.
    799     *
    800     * @param command the command name
    801     * @param message the complete message, including command name
    802     * @since 3.0
    803     */
    804    protected void fireCommandSent(String command, String message) {
    805        if (getCommandSupport().getListenerCount() > 0) {
    806            getCommandSupport().fireCommandSent(command, message);
    807        }
    808    }
    809
    810    /**
    811     * Create the CommandSupport instance if required
    812     */
    813    protected void createCommandSupport(){
    814        __commandSupport = new ProtocolCommandSupport(this);
    815    }
    816
    817    /**
    818     * Subclasses can override this if they need to provide their own
    819     * instance field for backwards compatibilty.
    820     *
    821     * @return the CommandSupport instance, may be {@code null}
    822     * @since 3.0
    823     */
    824    protected ProtocolCommandSupport getCommandSupport() {
    825        return __commandSupport;
    826    }
    827
    828    /**
    829     * Sets the proxy for use with all the connections.
    830     * The proxy is used for connections established after the
    831     * call to this method.
    832     *
    833     * @param proxy the new proxy for connections.
    834     * @since 3.2
    835     */
    836    public void setProxy(Proxy proxy) {
    837        setSocketFactory(new DefaultSocketFactory(proxy));
    838        connProxy = proxy;
    839    }
    840
    841    /**
    842     * Gets the proxy for use with all the connections.
    843     * @return the current proxy for connections.
    844     */
    845    public Proxy getProxy() {
    846        return connProxy;
    847    }
    848
    849    /**
    850     * Gets the charset name.
    851     *
    852     * @return the charset.
    853     * @since 3.3
    854     * @deprecated Since the code now requires Java 1.6 as a mininmum
    855     */
    856    @Deprecated
    857    public String getCharsetName() {
    858        return charset.name();
    859    }
    860
    861    /**
    862     * Gets the charset.
    863     *
    864     * @return the charset.
    865     * @since 3.3
    866     */
    867    public Charset getCharset() {
    868        return charset;
    869    }
    870
    871    /**
    872     * Sets the charset.
    873     *
    874     * @param charset the charset.
    875     * @since 3.3
    876     */
    877    public void setCharset(Charset charset) {
    878        this.charset = charset;
    879    }
    880
    881    /*
    882     *  N.B. Fields cannot be pulled up into a super-class without breaking binary compatibility,
    883     *  so the abstract method is needed to pass the instance to the methods which were moved here.
    884     */
    885}
    886
    887