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