DatagramSocketAdaptor.java revision 51b1b6997fd3f980076b8081f7f1165ccc2a4008
1/*
2 * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.nio.ch;
27
28import java.io.*;
29import java.net.*;
30import java.nio.*;
31import java.nio.channels.*;
32
33
34// Make a datagram-socket channel look like a datagram socket.
35//
36// The methods in this class are defined in exactly the same order as in
37// java.net.DatagramSocket so as to simplify tracking future changes to that
38// class.
39//
40
41public class DatagramSocketAdaptor
42    extends DatagramSocket
43{
44
45    // The channel being adapted
46    private final DatagramChannelImpl dc;
47
48    // Timeout "option" value for receives
49    private volatile int timeout = 0;
50
51    // ## super will create a useless impl
52    private DatagramSocketAdaptor(DatagramChannelImpl dc) throws IOException {
53        // Invoke the DatagramSocketAdaptor(SocketAddress) constructor,
54        // passing a dummy DatagramSocketImpl object to aovid any native
55        // resource allocation in super class and invoking our bind method
56        // before the dc field is initialized.
57        super(dummyDatagramSocket);
58        this.dc = dc;
59    }
60
61    public static DatagramSocket create(DatagramChannelImpl dc) {
62        try {
63            return new DatagramSocketAdaptor(dc);
64        } catch (IOException x) {
65            throw new Error(x);
66        }
67    }
68
69    private void connectInternal(SocketAddress remote)
70        throws SocketException
71    {
72        InetSocketAddress isa = Net.asInetSocketAddress(remote);
73        int port = isa.getPort();
74        if (port < 0 || port > 0xFFFF)
75            throw new IllegalArgumentException("connect: " + port);
76        if (remote == null)
77            throw new IllegalArgumentException("connect: null address");
78        if (isClosed())
79            return;
80        try {
81            dc.connect(remote);
82        } catch (Exception x) {
83            Net.translateToSocketException(x);
84        }
85    }
86
87    public void bind(SocketAddress local) throws SocketException {
88        try {
89            if (local == null)
90                local = new InetSocketAddress(0);
91            dc.bind(local);
92        } catch (Exception x) {
93            Net.translateToSocketException(x);
94        }
95    }
96
97    public void connect(InetAddress address, int port) {
98        try {
99            connectInternal(new InetSocketAddress(address, port));
100        } catch (SocketException x) {
101            // Yes, j.n.DatagramSocket really does this
102        }
103    }
104
105    public void connect(SocketAddress remote) throws SocketException {
106        if (remote == null)
107            throw new IllegalArgumentException("Address can't be null");
108        connectInternal(remote);
109    }
110
111    public void disconnect() {
112        try {
113            dc.disconnect();
114        } catch (IOException x) {
115            throw new Error(x);
116        }
117    }
118
119    public boolean isBound() {
120        return dc.localAddress() != null;
121    }
122
123    public boolean isConnected() {
124        return dc.remoteAddress() != null;
125    }
126
127    public InetAddress getInetAddress() {
128        return (isConnected()
129                ? Net.asInetSocketAddress(dc.remoteAddress()).getAddress()
130                : null);
131    }
132
133    public int getPort() {
134        return (isConnected()
135                ? Net.asInetSocketAddress(dc.remoteAddress()).getPort()
136                : -1);
137    }
138
139    public void send(DatagramPacket p) throws IOException {
140        synchronized (dc.blockingLock()) {
141            if (!dc.isBlocking())
142                throw new IllegalBlockingModeException();
143            try {
144                synchronized (p) {
145                    ByteBuffer bb = ByteBuffer.wrap(p.getData(),
146                                                    p.getOffset(),
147                                                    p.getLength());
148                    if (dc.isConnected()) {
149                        if (p.getAddress() == null) {
150                            // Legacy DatagramSocket will send in this case
151                            // and set address and port of the packet
152                            InetSocketAddress isa = (InetSocketAddress)
153                                                    dc.remoteAddress();
154                            p.setPort(isa.getPort());
155                            p.setAddress(isa.getAddress());
156                            dc.write(bb);
157                        } else {
158                            // Target address may not match connected address
159                            dc.send(bb, p.getSocketAddress());
160                        }
161                    } else {
162                        // Not connected so address must be valid or throw
163                        dc.send(bb, p.getSocketAddress());
164                    }
165                }
166            } catch (IOException x) {
167                Net.translateException(x);
168            }
169        }
170    }
171
172    // Must hold dc.blockingLock()
173    //
174    private SocketAddress receive(ByteBuffer bb) throws IOException {
175        if (timeout == 0) {
176            return dc.receive(bb);
177        }
178
179        // Implement timeout with a selector
180        SelectionKey sk = null;
181        Selector sel = null;
182        dc.configureBlocking(false);
183        try {
184            int n;
185            SocketAddress sender;
186            if ((sender = dc.receive(bb)) != null)
187                return sender;
188            sel = Util.getTemporarySelector(dc);
189            sk = dc.register(sel, SelectionKey.OP_READ);
190            long to = timeout;
191            for (;;) {
192                if (!dc.isOpen())
193                     throw new ClosedChannelException();
194                long st = System.currentTimeMillis();
195                int ns = sel.select(to);
196                if (ns > 0 && sk.isReadable()) {
197                    if ((sender = dc.receive(bb)) != null)
198                        return sender;
199                }
200                sel.selectedKeys().remove(sk);
201                to -= System.currentTimeMillis() - st;
202                if (to <= 0)
203                    throw new SocketTimeoutException();
204
205            }
206        } finally {
207            if (sk != null)
208                sk.cancel();
209            if (dc.isOpen())
210                dc.configureBlocking(true);
211            if (sel != null)
212                Util.releaseTemporarySelector(sel);
213        }
214    }
215
216    public void receive(DatagramPacket p) throws IOException {
217        synchronized (dc.blockingLock()) {
218            if (!dc.isBlocking())
219                throw new IllegalBlockingModeException();
220            try {
221                synchronized (p) {
222                    ByteBuffer bb = ByteBuffer.wrap(p.getData(),
223                                                    p.getOffset(),
224                                                    p.getLength());
225                    SocketAddress sender = receive(bb);
226                    p.setSocketAddress(sender);
227                    p.setLength(bb.position() - p.getOffset());
228                }
229            } catch (IOException x) {
230                Net.translateException(x);
231            }
232        }
233    }
234
235    public InetAddress getLocalAddress() {
236        if (isClosed())
237            return null;
238        SocketAddress local = dc.localAddress();
239        if (local == null)
240            local = new InetSocketAddress(0);
241        InetAddress result = ((InetSocketAddress)local).getAddress();
242        SecurityManager sm = System.getSecurityManager();
243        if (sm != null) {
244            try {
245                sm.checkConnect(result.getHostAddress(), -1);
246            } catch (SecurityException x) {
247                return new InetSocketAddress(0).getAddress();
248            }
249        }
250        return result;
251    }
252
253    public int getLocalPort() {
254        if (isClosed())
255            return -1;
256        try {
257            SocketAddress local = dc.getLocalAddress();
258            if (local != null) {
259                return ((InetSocketAddress)local).getPort();
260            }
261        } catch (Exception x) {
262        }
263        return 0;
264    }
265
266    public void setSoTimeout(int timeout) throws SocketException {
267        this.timeout = timeout;
268    }
269
270    public int getSoTimeout() throws SocketException {
271        return timeout;
272    }
273
274    private void setBooleanOption(SocketOption<Boolean> name, boolean value)
275        throws SocketException
276    {
277        try {
278            dc.setOption(name, value);
279        } catch (IOException x) {
280            Net.translateToSocketException(x);
281        }
282    }
283
284    private void setIntOption(SocketOption<Integer> name, int value)
285        throws SocketException
286    {
287        try {
288            dc.setOption(name, value);
289        } catch (IOException x) {
290            Net.translateToSocketException(x);
291        }
292    }
293
294    private boolean getBooleanOption(SocketOption<Boolean> name) throws SocketException {
295        try {
296            return dc.getOption(name).booleanValue();
297        } catch (IOException x) {
298            Net.translateToSocketException(x);
299            return false;       // keep compiler happy
300        }
301    }
302
303    private int getIntOption(SocketOption<Integer> name) throws SocketException {
304        try {
305            return dc.getOption(name).intValue();
306        } catch (IOException x) {
307            Net.translateToSocketException(x);
308            return -1;          // keep compiler happy
309        }
310    }
311
312    public void setSendBufferSize(int size) throws SocketException {
313        if (size <= 0)
314            throw new IllegalArgumentException("Invalid send size");
315        setIntOption(StandardSocketOptions.SO_SNDBUF, size);
316    }
317
318    public int getSendBufferSize() throws SocketException {
319        return getIntOption(StandardSocketOptions.SO_SNDBUF);
320    }
321
322    public void setReceiveBufferSize(int size) throws SocketException {
323        if (size <= 0)
324            throw new IllegalArgumentException("Invalid receive size");
325        setIntOption(StandardSocketOptions.SO_RCVBUF, size);
326    }
327
328    public int getReceiveBufferSize() throws SocketException {
329        return getIntOption(StandardSocketOptions.SO_RCVBUF);
330    }
331
332    public void setReuseAddress(boolean on) throws SocketException {
333        setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on);
334    }
335
336    public boolean getReuseAddress() throws SocketException {
337        return getBooleanOption(StandardSocketOptions.SO_REUSEADDR);
338
339    }
340
341    public void setBroadcast(boolean on) throws SocketException {
342        setBooleanOption(StandardSocketOptions.SO_BROADCAST, on);
343    }
344
345    public boolean getBroadcast() throws SocketException {
346        return getBooleanOption(StandardSocketOptions.SO_BROADCAST);
347    }
348
349    public void setTrafficClass(int tc) throws SocketException {
350        setIntOption(StandardSocketOptions.IP_TOS, tc);
351    }
352
353    public int getTrafficClass() throws SocketException {
354        return getIntOption(StandardSocketOptions.IP_TOS);
355    }
356
357    public void close() {
358        try {
359            dc.close();
360        } catch (IOException x) {
361            throw new Error(x);
362        }
363    }
364
365    public boolean isClosed() {
366        return !dc.isOpen();
367    }
368
369    public DatagramChannel getChannel() {
370        return dc;
371    }
372
373   /*
374    * A dummy implementation of DatagramSocketImpl that can be passed to the
375    * DatagramSocket constructor so that no native resources are allocated in
376    * super class.
377    */
378   private static final DatagramSocketImpl dummyDatagramSocket
379       = new DatagramSocketImpl()
380   {
381       protected void create() throws SocketException {}
382
383       protected void bind(int lport, InetAddress laddr) throws SocketException {}
384
385       protected void send(DatagramPacket p) throws IOException {}
386
387       protected int peek(InetAddress i) throws IOException { return 0; }
388
389       protected int peekData(DatagramPacket p) throws IOException { return 0; }
390
391       protected void receive(DatagramPacket p) throws IOException {}
392
393       protected void setTTL(byte ttl) throws IOException {}
394
395       protected byte getTTL() throws IOException { return 0; }
396
397       protected void setTimeToLive(int ttl) throws IOException {}
398
399       protected int getTimeToLive() throws IOException { return 0;}
400
401       protected void join(InetAddress inetaddr) throws IOException {}
402
403       protected void leave(InetAddress inetaddr) throws IOException {}
404
405       protected void joinGroup(SocketAddress mcastaddr,
406                                 NetworkInterface netIf) throws IOException {}
407
408       protected void leaveGroup(SocketAddress mcastaddr,
409                                 NetworkInterface netIf) throws IOException {}
410
411       protected void close() {}
412
413       public Object getOption(int optID) throws SocketException { return null;}
414
415       public void setOption(int optID, Object value) throws SocketException {}
416   };
417}
418