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