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 of client objects accessing datagram sockets. It is meant to be subclassed to avoid
027 * having to rewrite the same code over and over again to open a socket, close a socket, set timeouts, etc. Of special note is the
028 * {@link #setDatagramSocketFactory setDatagramSocketFactory } method, which allows you to control the type of DatagramSocket the DatagramSocketClient creates
029 * for network communications. This is especially useful for adding things like proxy support as well as better support for applets. For example, you could
030 * create a {@link org.apache.commons.net.DatagramSocketFactory} that requests browser security capabilities before creating a socket. All classes derived from
031 * DatagramSocketClient should use the {@link #_socketFactory_ _socketFactory_ } member variable to create DatagramSocket instances rather than instantiating
032 * them by directly invoking a constructor. By honoring this contract you guarantee that a user will always be able to provide his own Socket implementations by
033 * substituting his own SocketFactory.
034 *
035 *
036 * @see DatagramSocketFactory
037 */
038
039public abstract class DatagramSocketClient {
040    /**
041     * The default DatagramSocketFactory shared by all DatagramSocketClient instances.
042     */
043    private static final DatagramSocketFactory DEFAULT_SOCKET_FACTORY = new DefaultDatagramSocketFactory();
044
045    /**
046     * Charset to use for byte IO.
047     */
048    private Charset charset = Charset.defaultCharset();
049
050    /** The timeout to use after opening a socket. */
051    protected int _timeout_;
052
053    /** The datagram socket used for the connection. */
054    protected DatagramSocket _socket_;
055
056    /**
057     * A status variable indicating if the client's socket is currently open.
058     */
059    protected boolean _isOpen_;
060
061    /** The datagram socket's DatagramSocketFactory. */
062    protected DatagramSocketFactory _socketFactory_;
063
064    /**
065     * Default constructor for DatagramSocketClient. Initializes _socket_ to null, _timeout_ to 0, and _isOpen_ to false.
066     */
067    public DatagramSocketClient() {
068        _socket_ = null;
069        _timeout_ = 0;
070        _isOpen_ = false;
071        _socketFactory_ = DEFAULT_SOCKET_FACTORY;
072    }
073
074    /**
075     * Closes the DatagramSocket used for the connection. You should call this method after you've finished using the class instance and also before you call
076     * {@link #open open() } again. _isOpen_ is set to false and _socket_ is set to null.
077     */
078    public void close() {
079        if (_socket_ != null) {
080            _socket_.close();
081        }
082        _socket_ = null;
083        _isOpen_ = false;
084    }
085
086    /**
087     * Gets the charset.
088     *
089     * @return the charset.
090     * @since 3.3
091     */
092    public Charset getCharset() {
093        return charset;
094    }
095
096    /**
097     * Gets the charset name.
098     *
099     * @return the charset name.
100     * @since 3.3
101     * @deprecated Use {@link #getCharset()} instead
102     */
103    @Deprecated
104    public String getCharsetName() {
105        return charset.name();
106    }
107
108    /**
109     * Returns the default timeout in milliseconds that is used when opening a socket.
110     *
111     * @return The default timeout in milliseconds that is used when opening a socket.
112     */
113    public int getDefaultTimeout() {
114        return _timeout_;
115    }
116
117    /**
118     * Returns the local address to which the client's socket is bound. If you call this method when the client socket is not open, a NullPointerException is
119     * thrown.
120     *
121     * @return The local address to which the client's socket is bound.
122     */
123    public InetAddress getLocalAddress() {
124        return _socket_.getLocalAddress();
125    }
126
127    /**
128     * Returns the port number of the open socket on the local host used for the connection. If you call this method when the client socket is not open, a
129     * NullPointerException is thrown.
130     *
131     * @return The port number of the open socket on the local host used for the connection.
132     */
133    public int getLocalPort() {
134        return _socket_.getLocalPort();
135    }
136
137    /**
138     * Returns the timeout in milliseconds of the currently opened socket. If you call this method when the client socket is not open, a NullPointerException is
139     * thrown.
140     *
141     * @return The timeout in milliseconds of the currently opened socket.
142     * @throws SocketException if an error getting the timeout
143     */
144    public int getSoTimeout() throws SocketException {
145        return _socket_.getSoTimeout();
146    }
147
148    /**
149     * Returns true if the client has a currently open socket.
150     *
151     * @return True if the client has a currently open socket, false otherwise.
152     */
153    public boolean isOpen() {
154        return _isOpen_;
155    }
156
157    /**
158     * Opens a DatagramSocket on the local host at the first available port. Also sets the timeout on the socket to the default timeout set by
159     * {@link #setDefaultTimeout setDefaultTimeout() }.
160     * <p>
161     * _isOpen_ is set to true after calling this method and _socket_ is set to the newly opened socket.
162     *
163     * @throws SocketException If the socket could not be opened or the timeout could not be set.
164     */
165    public void open() throws SocketException {
166        _socket_ = _socketFactory_.createDatagramSocket();
167        _socket_.setSoTimeout(_timeout_);
168        _isOpen_ = true;
169    }
170
171    /**
172     * Opens a DatagramSocket on the local host at a specified port. Also sets the timeout on the socket to the default timeout set by {@link #setDefaultTimeout
173     * setDefaultTimeout() }.
174     * <p>
175     * _isOpen_ is set to true after calling this method and _socket_ is set to the newly opened socket.
176     *
177     * @param port The port to use for the socket.
178     * @throws SocketException If the socket could not be opened or the timeout could not be set.
179     */
180    public void open(final int port) throws SocketException {
181        _socket_ = _socketFactory_.createDatagramSocket(port);
182        _socket_.setSoTimeout(_timeout_);
183        _isOpen_ = true;
184    }
185
186    /**
187     * Opens a DatagramSocket at the specified address on the local host at a specified port. Also sets the timeout on the socket to the default timeout set by
188     * {@link #setDefaultTimeout setDefaultTimeout() }.
189     * <p>
190     * _isOpen_ is set to true after calling this method and _socket_ is set to the newly opened socket.
191     *
192     * @param port  The port to use for the socket.
193     * @param laddr The local address to use.
194     * @throws SocketException If the socket could not be opened or the timeout could not be set.
195     */
196    public void open(final int port, final InetAddress laddr) throws SocketException {
197        _socket_ = _socketFactory_.createDatagramSocket(port, laddr);
198        _socket_.setSoTimeout(_timeout_);
199        _isOpen_ = true;
200    }
201
202    /**
203     * Sets the charset.
204     *
205     * @param charset the charset.
206     * @since 3.3
207     */
208    public void setCharset(final Charset charset) {
209        this.charset = charset;
210    }
211
212    /**
213     * Sets the DatagramSocketFactory used by the DatagramSocketClient to open DatagramSockets. If the factory value is null, then a default factory is used
214     * (only do this to reset the factory after having previously altered it).
215     *
216     * @param factory The new DatagramSocketFactory the DatagramSocketClient should use.
217     */
218    public void setDatagramSocketFactory(final DatagramSocketFactory factory) {
219        if (factory == null) {
220            _socketFactory_ = DEFAULT_SOCKET_FACTORY;
221        } else {
222            _socketFactory_ = factory;
223        }
224    }
225
226    /**
227     * Set the default timeout in milliseconds to use when opening a socket. After a call to open, the timeout for the socket is set using this value. This
228     * method should be used prior to a call to {@link #open open()} and should not be confused with {@link #setSoTimeout setSoTimeout()} which operates on the
229     * currently open socket. _timeout_ contains the new timeout value.
230     *
231     * @param timeout The timeout in milliseconds to use for the datagram socket connection.
232     */
233    public void setDefaultTimeout(final int timeout) {
234        _timeout_ = timeout;
235    }
236
237    /**
238     * Set the timeout in milliseconds of a currently open connection. Only call this method after a connection has been opened by {@link #open open()}.
239     *
240     * @param timeout The timeout in milliseconds to use for the currently open datagram socket connection.
241     * @throws SocketException if an error setting the timeout
242     */
243    public void setSoTimeout(final int timeout) throws SocketException {
244        _socket_.setSoTimeout(timeout);
245    }
246}