001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.net;
019
020import java.net.DatagramSocket;
021import java.net.InetAddress;
022import java.net.SocketException;
023import java.nio.charset.Charset;
024
025/***
026 * The DatagramSocketClient provides the basic operations that are required
027 * of client objects accessing datagram sockets.  It is meant to be
028 * subclassed to avoid having to rewrite the same code over and over again
029 * to open a socket, close a socket, set timeouts, etc.  Of special note
030 * is the {@link #setDatagramSocketFactory  setDatagramSocketFactory }
031 * method, which allows you to control the type of DatagramSocket the
032 * DatagramSocketClient creates for network communications.  This is
033 * especially useful for adding things like proxy support as well as better
034 * support for applets.  For
035 * example, you could create a
036 * {@link org.apache.commons.net.DatagramSocketFactory}
037 *  that
038 * requests browser security capabilities before creating a socket.
039 * All classes derived from DatagramSocketClient should use the
040 * {@link #_socketFactory_  _socketFactory_ } member variable to
041 * create DatagramSocket instances rather than instantiating
042 * them by directly invoking a constructor.  By honoring this contract
043 * you guarantee that a user will always be able to provide his own
044 * Socket implementations by substituting his own SocketFactory.
045 * <p>
046 * <p>
047 * @see DatagramSocketFactory
048 ***/
049
050public abstract class DatagramSocketClient
051{
052    /***
053     * The default DatagramSocketFactory shared by all DatagramSocketClient
054     * instances.
055     ***/
056    private static final DatagramSocketFactory __DEFAULT_SOCKET_FACTORY =
057        new DefaultDatagramSocketFactory();
058
059    /**
060     * Charset to use for byte IO.
061     */
062    private Charset charset = Charset.defaultCharset();
063
064    /*** The timeout to use after opening a socket. ***/
065    protected int _timeout_;
066
067    /*** The datagram socket used for the connection. ***/
068    protected DatagramSocket _socket_;
069
070    /***
071     * A status variable indicating if the client's socket is currently open.
072     ***/
073    protected boolean _isOpen_;
074
075    /*** The datagram socket's DatagramSocketFactory. ***/
076    protected DatagramSocketFactory _socketFactory_;
077
078    /***
079     * Default constructor for DatagramSocketClient.  Initializes
080     * _socket_ to null, _timeout_ to 0, and _isOpen_ to false.
081     ***/
082    public DatagramSocketClient()
083    {
084        _socket_ = null;
085        _timeout_ = 0;
086        _isOpen_ = false;
087        _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
088    }
089
090
091    /***
092     * Opens a DatagramSocket on the local host at the first available port.
093     * Also sets the timeout on the socket to the default timeout set
094     * by {@link #setDefaultTimeout  setDefaultTimeout() }.
095     * <p>
096     * _isOpen_ is set to true after calling this method and _socket_
097     * is set to the newly opened socket.
098     * <p>
099     * @exception SocketException If the socket could not be opened or the
100     *   timeout could not be set.
101     ***/
102    public void open() throws SocketException
103    {
104        _socket_ = _socketFactory_.createDatagramSocket();
105        _socket_.setSoTimeout(_timeout_);
106        _isOpen_ = true;
107    }
108
109
110    /***
111     * Opens a DatagramSocket on the local host at a specified port.
112     * Also sets the timeout on the socket to the default timeout set
113     * by {@link #setDefaultTimeout  setDefaultTimeout() }.
114     * <p>
115     * _isOpen_ is set to true after calling this method and _socket_
116     * is set to the newly opened socket.
117     * <p>
118     * @param port The port to use for the socket.
119     * @exception SocketException If the socket could not be opened or the
120     *   timeout could not be set.
121     ***/
122    public void open(int port) throws SocketException
123    {
124        _socket_ = _socketFactory_.createDatagramSocket(port);
125        _socket_.setSoTimeout(_timeout_);
126        _isOpen_ = true;
127    }
128
129
130    /***
131     * Opens a DatagramSocket at the specified address on the local host
132     * at a specified port.
133     * Also sets the timeout on the socket to the default timeout set
134     * by {@link #setDefaultTimeout  setDefaultTimeout() }.
135     * <p>
136     * _isOpen_ is set to true after calling this method and _socket_
137     * is set to the newly opened socket.
138     * <p>
139     * @param port The port to use for the socket.
140     * @param laddr  The local address to use.
141     * @exception SocketException If the socket could not be opened or the
142     *   timeout could not be set.
143     ***/
144    public void open(int port, InetAddress laddr) throws SocketException
145    {
146        _socket_ = _socketFactory_.createDatagramSocket(port, laddr);
147        _socket_.setSoTimeout(_timeout_);
148        _isOpen_ = true;
149    }
150
151
152
153    /***
154     * Closes the DatagramSocket used for the connection.
155     * You should call this method after you've finished using the class
156     * instance and also before you call {@link #open open() }
157     * again.   _isOpen_ is set to false and  _socket_ is set to null.
158     * If you call this method when the client socket is not open,
159     * a NullPointerException is thrown.
160     ***/
161    public void close()
162    {
163        if (_socket_ != null) {
164            _socket_.close();
165        }
166        _socket_ = null;
167        _isOpen_ = false;
168    }
169
170
171    /***
172     * Returns true if the client has a currently open socket.
173     * <p>
174     * @return True if the client has a curerntly open socket, false otherwise.
175     ***/
176    public boolean isOpen()
177    {
178        return _isOpen_;
179    }
180
181
182    /***
183     * Set the default timeout in milliseconds to use when opening a socket.
184     * After a call to open, the timeout for the socket is set using this value.
185     * This method should be used prior to a call to {@link #open open()}
186     * and should not be confused with {@link #setSoTimeout setSoTimeout()}
187     * which operates on the currently open socket.  _timeout_ contains
188     * the new timeout value.
189     * <p>
190     * @param timeout  The timeout in milliseconds to use for the datagram socket
191     *                 connection.
192     ***/
193    public void setDefaultTimeout(int timeout)
194    {
195        _timeout_ = timeout;
196    }
197
198
199    /***
200     * Returns the default timeout in milliseconds that is used when
201     * opening a socket.
202     * <p>
203     * @return The default timeout in milliseconds that is used when
204     *         opening a socket.
205     ***/
206    public int getDefaultTimeout()
207    {
208        return _timeout_;
209    }
210
211
212    /***
213     * Set the timeout in milliseconds of a currently open connection.
214     * Only call this method after a connection has been opened
215     * by {@link #open open()}.
216     * <p>
217     * @param timeout  The timeout in milliseconds to use for the currently
218     *                 open datagram socket connection.
219     ***/
220    public void setSoTimeout(int timeout) throws SocketException
221    {
222        _socket_.setSoTimeout(timeout);
223    }
224
225
226    /***
227     * Returns the timeout in milliseconds of the currently opened socket.
228     * If you call this method when the client socket is not open,
229     * a NullPointerException is thrown.
230     * <p>
231     * @return The timeout in milliseconds of the currently opened socket.
232     ***/
233    public int getSoTimeout() throws SocketException
234    {
235        return _socket_.getSoTimeout();
236    }
237
238
239    /***
240     * Returns the port number of the open socket on the local host used
241     * for the connection.  If you call this method when the client socket
242     * is not open, a NullPointerException is thrown.
243     * <p>
244     * @return The port number of the open socket on the local host used
245     *         for the connection.
246     ***/
247    public int getLocalPort()
248    {
249        return _socket_.getLocalPort();
250    }
251
252
253    /***
254     * Returns the local address to which the client's socket is bound.
255     * If you call this method when the client socket is not open, a
256     * NullPointerException is thrown.
257     * <p>
258     * @return The local address to which the client's socket is bound.
259     ***/
260    public InetAddress getLocalAddress()
261    {
262        return _socket_.getLocalAddress();
263    }
264
265
266    /***
267     * Sets the DatagramSocketFactory used by the DatagramSocketClient
268     * to open DatagramSockets.  If the factory value is null, then a default
269     * factory is used (only do this to reset the factory after having
270     * previously altered it).
271     * <p>
272     * @param factory  The new DatagramSocketFactory the DatagramSocketClient
273     * should use.
274     ***/
275    public void setDatagramSocketFactory(DatagramSocketFactory factory)
276    {
277        if (factory == null) {
278            _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
279        } else {
280            _socketFactory_ = factory;
281        }
282    }
283
284    /**
285     * Gets the charset name.
286     *
287     * @return the charset name.
288     * @since 3.3
289     * TODO Will be deprecated once the code requires Java 1.6 as a mininmum
290     */
291    public String getCharsetName() {
292        return charset.name();
293    }
294
295    /**
296     * Gets the charset.
297     *
298     * @return the charset.
299     * @since 3.3
300     */
301    public Charset getCharset() {
302        return charset;
303    }
304
305    /**
306     * Sets the charset.
307     *
308     * @param charset the charset.
309     * @since 3.3
310     */
311    public void setCharset(Charset charset) {
312        this.charset = charset;
313    }
314}