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