View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.net;
19  
20  import java.io.Closeable;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.OutputStream;
24  import java.net.InetAddress;
25  import java.net.InetSocketAddress;
26  import java.net.Proxy;
27  import java.net.Socket;
28  import java.net.SocketException;
29  import java.nio.charset.Charset;
30  
31  import javax.net.ServerSocketFactory;
32  import javax.net.SocketFactory;
33  
34  
35  /**
36   * The SocketClient provides the basic operations that are required of
37   * client objects accessing sockets.  It is meant to be
38   * subclassed to avoid having to rewrite the same code over and over again
39   * to open a socket, close a socket, set timeouts, etc.  Of special note
40   * is the {@link #setSocketFactory  setSocketFactory }
41   * method, which allows you to control the type of Socket the SocketClient
42   * creates for initiating network connections.  This is especially useful
43   * for adding SSL or proxy support as well as better support for applets.  For
44   * example, you could create a
45   * {@link javax.net.SocketFactory} that
46   * requests browser security capabilities before creating a socket.
47   * All classes derived from SocketClient should use the
48   * {@link #_socketFactory_  _socketFactory_ } member variable to
49   * create Socket and ServerSocket instances rather than instantiating
50   * them by directly invoking a constructor.  By honoring this contract
51   * you guarantee that a user will always be able to provide his own
52   * Socket implementations by substituting his own SocketFactory.
53   * @see SocketFactory
54   */
55  public abstract class SocketClient
56  {
57      /**
58       * The end of line character sequence used by most IETF protocols.  That
59       * is a carriage return followed by a newline: "\r\n"
60       */
61      public static final String NETASCII_EOL = "\r\n";
62  
63      /** The default SocketFactory shared by all SocketClient instances. */
64      private static final SocketFactory __DEFAULT_SOCKET_FACTORY =
65              SocketFactory.getDefault();
66  
67      /** The default {@link ServerSocketFactory} */
68      private static final ServerSocketFactory __DEFAULT_SERVER_SOCKET_FACTORY =
69              ServerSocketFactory.getDefault();
70  
71      /**
72       * A ProtocolCommandSupport object used to manage the registering of
73       * ProtocolCommandListeners and the firing of ProtocolCommandEvents.
74       */
75      private ProtocolCommandSupport __commandSupport;
76  
77      /** The timeout to use after opening a socket. */
78      protected int _timeout_;
79  
80      /** The socket used for the connection. */
81      protected Socket _socket_;
82  
83      /** The hostname used for the connection (null = no hostname supplied). */
84      protected String _hostname_;
85  
86      /** The default port the client should connect to. */
87      protected int _defaultPort_;
88  
89      /** The socket's InputStream. */
90      protected InputStream _input_;
91  
92      /** The socket's OutputStream. */
93      protected OutputStream _output_;
94  
95      /** The socket's SocketFactory. */
96      protected SocketFactory _socketFactory_;
97  
98      /** The socket's ServerSocket Factory. */
99      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