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