1 /* |
|
2 * Copyright (c) 2009, 2011, 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 package sun.nio.ch; |
|
26 |
|
27 import java.net.SocketAddress; |
|
28 import java.net.InetSocketAddress; |
|
29 import java.net.InetAddress; |
|
30 import java.io.FileDescriptor; |
|
31 import java.io.IOException; |
|
32 import java.util.Collections; |
|
33 import java.util.Set; |
|
34 import java.util.HashSet; |
|
35 import java.nio.channels.SelectionKey; |
|
36 import java.nio.channels.ClosedChannelException; |
|
37 import java.nio.channels.NotYetBoundException; |
|
38 import java.nio.channels.spi.SelectorProvider; |
|
39 import com.sun.nio.sctp.IllegalUnbindException; |
|
40 import com.sun.nio.sctp.SctpChannel; |
|
41 import com.sun.nio.sctp.SctpServerChannel; |
|
42 import com.sun.nio.sctp.SctpSocketOption; |
|
43 import com.sun.nio.sctp.SctpStandardSocketOptions; |
|
44 |
|
45 /** |
|
46 * An implementation of SctpServerChannel |
|
47 */ |
|
48 public class SctpServerChannelImpl extends SctpServerChannel |
|
49 implements SelChImpl |
|
50 { |
|
51 private final FileDescriptor fd; |
|
52 |
|
53 private final int fdVal; |
|
54 |
|
55 /* IDs of native thread doing accept, for signalling */ |
|
56 private volatile long thread = 0; |
|
57 |
|
58 /* Lock held by thread currently blocked in this channel */ |
|
59 private final Object lock = new Object(); |
|
60 |
|
61 /* Lock held by any thread that modifies the state fields declared below |
|
62 * DO NOT invoke a blocking I/O operation while holding this lock! */ |
|
63 private final Object stateLock = new Object(); |
|
64 |
|
65 private enum ChannelState { |
|
66 UNINITIALIZED, |
|
67 INUSE, |
|
68 KILLPENDING, |
|
69 KILLED, |
|
70 } |
|
71 /* -- The following fields are protected by stateLock -- */ |
|
72 private ChannelState state = ChannelState.UNINITIALIZED; |
|
73 |
|
74 /* Binding: Once bound the port will remain constant. */ |
|
75 int port = -1; |
|
76 private HashSet<InetSocketAddress> localAddresses = new HashSet<InetSocketAddress>(); |
|
77 /* Has the channel been bound to the wildcard address */ |
|
78 private boolean wildcard; /* false */ |
|
79 |
|
80 /* -- End of fields protected by stateLock -- */ |
|
81 |
|
82 /** |
|
83 * Initializes a new instance of this class. |
|
84 */ |
|
85 public SctpServerChannelImpl(SelectorProvider provider) |
|
86 throws IOException { |
|
87 //TODO: update provider remove public modifier |
|
88 super(provider); |
|
89 this.fd = SctpNet.socket(true); |
|
90 this.fdVal = IOUtil.fdVal(fd); |
|
91 this.state = ChannelState.INUSE; |
|
92 } |
|
93 |
|
94 @Override |
|
95 public SctpServerChannel bind(SocketAddress local, int backlog) |
|
96 throws IOException { |
|
97 synchronized (lock) { |
|
98 synchronized (stateLock) { |
|
99 if (!isOpen()) |
|
100 throw new ClosedChannelException(); |
|
101 if (isBound()) |
|
102 SctpNet.throwAlreadyBoundException(); |
|
103 |
|
104 InetSocketAddress isa = (local == null) ? |
|
105 new InetSocketAddress(0) : Net.checkAddress(local); |
|
106 SecurityManager sm = System.getSecurityManager(); |
|
107 if (sm != null) |
|
108 sm.checkListen(isa.getPort()); |
|
109 Net.bind(fd, isa.getAddress(), isa.getPort()); |
|
110 |
|
111 InetSocketAddress boundIsa = Net.localAddress(fd); |
|
112 port = boundIsa.getPort(); |
|
113 localAddresses.add(isa); |
|
114 if (isa.getAddress().isAnyLocalAddress()) |
|
115 wildcard = true; |
|
116 |
|
117 SctpNet.listen(fdVal, backlog < 1 ? 50 : backlog); |
|
118 } |
|
119 } |
|
120 return this; |
|
121 } |
|
122 |
|
123 @Override |
|
124 public SctpServerChannel bindAddress(InetAddress address) |
|
125 throws IOException { |
|
126 return bindUnbindAddress(address, true); |
|
127 } |
|
128 |
|
129 @Override |
|
130 public SctpServerChannel unbindAddress(InetAddress address) |
|
131 throws IOException { |
|
132 return bindUnbindAddress(address, false); |
|
133 } |
|
134 |
|
135 private SctpServerChannel bindUnbindAddress(InetAddress address, boolean add) |
|
136 throws IOException { |
|
137 if (address == null) |
|
138 throw new IllegalArgumentException(); |
|
139 |
|
140 synchronized (lock) { |
|
141 synchronized (stateLock) { |
|
142 if (!isOpen()) |
|
143 throw new ClosedChannelException(); |
|
144 if (!isBound()) |
|
145 throw new NotYetBoundException(); |
|
146 if (wildcard) |
|
147 throw new IllegalStateException( |
|
148 "Cannot add or remove addresses from a channel that is bound to the wildcard address"); |
|
149 if (address.isAnyLocalAddress()) |
|
150 throw new IllegalArgumentException( |
|
151 "Cannot add or remove the wildcard address"); |
|
152 if (add) { |
|
153 for (InetSocketAddress addr : localAddresses) { |
|
154 if (addr.getAddress().equals(address)) { |
|
155 SctpNet.throwAlreadyBoundException(); |
|
156 } |
|
157 } |
|
158 } else { /*removing */ |
|
159 /* Verify that there is more than one address |
|
160 * and that address is already bound */ |
|
161 if (localAddresses.size() <= 1) |
|
162 throw new IllegalUnbindException("Cannot remove address from a channel with only one address bound"); |
|
163 boolean foundAddress = false; |
|
164 for (InetSocketAddress addr : localAddresses) { |
|
165 if (addr.getAddress().equals(address)) { |
|
166 foundAddress = true; |
|
167 break; |
|
168 } |
|
169 } |
|
170 if (!foundAddress ) |
|
171 throw new IllegalUnbindException("Cannot remove address from a channel that is not bound to that address"); |
|
172 } |
|
173 |
|
174 SctpNet.bindx(fdVal, new InetAddress[]{address}, port, add); |
|
175 |
|
176 /* Update our internal Set to reflect the addition/removal */ |
|
177 if (add) |
|
178 localAddresses.add(new InetSocketAddress(address, port)); |
|
179 else { |
|
180 for (InetSocketAddress addr : localAddresses) { |
|
181 if (addr.getAddress().equals(address)) { |
|
182 localAddresses.remove(addr); |
|
183 break; |
|
184 } |
|
185 } |
|
186 } |
|
187 } |
|
188 } |
|
189 return this; |
|
190 } |
|
191 |
|
192 private boolean isBound() { |
|
193 synchronized (stateLock) { |
|
194 return port == -1 ? false : true; |
|
195 } |
|
196 } |
|
197 |
|
198 private void acceptCleanup() throws IOException { |
|
199 synchronized (stateLock) { |
|
200 thread = 0; |
|
201 if (state == ChannelState.KILLPENDING) |
|
202 kill(); |
|
203 } |
|
204 } |
|
205 |
|
206 @Override |
|
207 public SctpChannel accept() throws IOException { |
|
208 synchronized (lock) { |
|
209 if (!isOpen()) |
|
210 throw new ClosedChannelException(); |
|
211 if (!isBound()) |
|
212 throw new NotYetBoundException(); |
|
213 SctpChannel sc = null; |
|
214 |
|
215 int n = 0; |
|
216 FileDescriptor newfd = new FileDescriptor(); |
|
217 InetSocketAddress[] isaa = new InetSocketAddress[1]; |
|
218 |
|
219 try { |
|
220 begin(); |
|
221 if (!isOpen()) |
|
222 return null; |
|
223 thread = NativeThread.current(); |
|
224 for (;;) { |
|
225 n = accept0(fd, newfd, isaa); |
|
226 if ((n == IOStatus.INTERRUPTED) && isOpen()) |
|
227 continue; |
|
228 break; |
|
229 } |
|
230 } finally { |
|
231 acceptCleanup(); |
|
232 end(n > 0); |
|
233 assert IOStatus.check(n); |
|
234 } |
|
235 |
|
236 if (n < 1) |
|
237 return null; |
|
238 |
|
239 IOUtil.configureBlocking(newfd, true); |
|
240 InetSocketAddress isa = isaa[0]; |
|
241 sc = new SctpChannelImpl(provider(), newfd); |
|
242 |
|
243 SecurityManager sm = System.getSecurityManager(); |
|
244 if (sm != null) |
|
245 sm.checkAccept(isa.getAddress().getHostAddress(), |
|
246 isa.getPort()); |
|
247 |
|
248 return sc; |
|
249 } |
|
250 } |
|
251 |
|
252 @Override |
|
253 protected void implConfigureBlocking(boolean block) throws IOException { |
|
254 IOUtil.configureBlocking(fd, block); |
|
255 } |
|
256 |
|
257 @Override |
|
258 public void implCloseSelectableChannel() throws IOException { |
|
259 synchronized (stateLock) { |
|
260 SctpNet.preClose(fdVal); |
|
261 if (thread != 0) |
|
262 NativeThread.signal(thread); |
|
263 if (!isRegistered()) |
|
264 kill(); |
|
265 } |
|
266 } |
|
267 |
|
268 @Override |
|
269 public void kill() throws IOException { |
|
270 synchronized (stateLock) { |
|
271 if (state == ChannelState.KILLED) |
|
272 return; |
|
273 if (state == ChannelState.UNINITIALIZED) { |
|
274 state = ChannelState.KILLED; |
|
275 return; |
|
276 } |
|
277 assert !isOpen() && !isRegistered(); |
|
278 |
|
279 // Postpone the kill if there is a thread in accept |
|
280 if (thread == 0) { |
|
281 SctpNet.close(fdVal); |
|
282 state = ChannelState.KILLED; |
|
283 } else { |
|
284 state = ChannelState.KILLPENDING; |
|
285 } |
|
286 } |
|
287 } |
|
288 |
|
289 @Override |
|
290 public FileDescriptor getFD() { |
|
291 return fd; |
|
292 } |
|
293 |
|
294 @Override |
|
295 public int getFDVal() { |
|
296 return fdVal; |
|
297 } |
|
298 |
|
299 /** |
|
300 * Translates native poll revent ops into a ready operation ops |
|
301 */ |
|
302 private boolean translateReadyOps(int ops, int initialOps, |
|
303 SelectionKeyImpl sk) { |
|
304 int intOps = sk.nioInterestOps(); |
|
305 int oldOps = sk.nioReadyOps(); |
|
306 int newOps = initialOps; |
|
307 |
|
308 if ((ops & PollArrayWrapper.POLLNVAL) != 0) { |
|
309 /* This should only happen if this channel is pre-closed while a |
|
310 * selection operation is in progress |
|
311 * ## Throw an error if this channel has not been pre-closed */ |
|
312 return false; |
|
313 } |
|
314 |
|
315 if ((ops & (PollArrayWrapper.POLLERR |
|
316 | PollArrayWrapper.POLLHUP)) != 0) { |
|
317 newOps = intOps; |
|
318 sk.nioReadyOps(newOps); |
|
319 return (newOps & ~oldOps) != 0; |
|
320 } |
|
321 |
|
322 if (((ops & PollArrayWrapper.POLLIN) != 0) && |
|
323 ((intOps & SelectionKey.OP_ACCEPT) != 0)) |
|
324 newOps |= SelectionKey.OP_ACCEPT; |
|
325 |
|
326 sk.nioReadyOps(newOps); |
|
327 return (newOps & ~oldOps) != 0; |
|
328 } |
|
329 |
|
330 @Override |
|
331 public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) { |
|
332 return translateReadyOps(ops, sk.nioReadyOps(), sk); |
|
333 } |
|
334 |
|
335 @Override |
|
336 public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) { |
|
337 return translateReadyOps(ops, 0, sk); |
|
338 } |
|
339 |
|
340 @Override |
|
341 public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) { |
|
342 int newOps = 0; |
|
343 |
|
344 /* Translate ops */ |
|
345 if ((ops & SelectionKey.OP_ACCEPT) != 0) |
|
346 newOps |= PollArrayWrapper.POLLIN; |
|
347 /* Place ops into pollfd array */ |
|
348 sk.selector.putEventOps(sk, newOps); |
|
349 |
|
350 } |
|
351 |
|
352 @Override |
|
353 public <T> SctpServerChannel setOption(SctpSocketOption<T> name, T value) |
|
354 throws IOException { |
|
355 if (name == null) |
|
356 throw new NullPointerException(); |
|
357 if (!supportedOptions().contains(name)) |
|
358 throw new UnsupportedOperationException("'" + name + "' not supported"); |
|
359 |
|
360 synchronized (stateLock) { |
|
361 if (!isOpen()) |
|
362 throw new ClosedChannelException(); |
|
363 |
|
364 SctpNet.setSocketOption(fdVal, name, value, 0 /*oneToOne*/); |
|
365 return this; |
|
366 } |
|
367 } |
|
368 |
|
369 @Override |
|
370 @SuppressWarnings("unchecked") |
|
371 public <T> T getOption(SctpSocketOption<T> name) throws IOException { |
|
372 if (name == null) |
|
373 throw new NullPointerException(); |
|
374 if (!supportedOptions().contains(name)) |
|
375 throw new UnsupportedOperationException("'" + name + "' not supported"); |
|
376 |
|
377 synchronized (stateLock) { |
|
378 if (!isOpen()) |
|
379 throw new ClosedChannelException(); |
|
380 |
|
381 return (T) SctpNet.getSocketOption(fdVal, name, 0 /*oneToOne*/); |
|
382 } |
|
383 } |
|
384 |
|
385 private static class DefaultOptionsHolder { |
|
386 static final Set<SctpSocketOption<?>> defaultOptions = defaultOptions(); |
|
387 |
|
388 private static Set<SctpSocketOption<?>> defaultOptions() { |
|
389 HashSet<SctpSocketOption<?>> set = new HashSet<SctpSocketOption<?>>(1); |
|
390 set.add(SctpStandardSocketOptions.SCTP_INIT_MAXSTREAMS); |
|
391 return Collections.unmodifiableSet(set); |
|
392 } |
|
393 } |
|
394 |
|
395 @Override |
|
396 public final Set<SctpSocketOption<?>> supportedOptions() { |
|
397 return DefaultOptionsHolder.defaultOptions; |
|
398 } |
|
399 |
|
400 @Override |
|
401 public Set<SocketAddress> getAllLocalAddresses() |
|
402 throws IOException { |
|
403 synchronized (stateLock) { |
|
404 if (!isOpen()) |
|
405 throw new ClosedChannelException(); |
|
406 if (!isBound()) |
|
407 return Collections.emptySet(); |
|
408 |
|
409 return SctpNet.getLocalAddresses(fdVal); |
|
410 } |
|
411 } |
|
412 |
|
413 /* Native */ |
|
414 private static native void initIDs(); |
|
415 |
|
416 private static native int accept0(FileDescriptor ssfd, |
|
417 FileDescriptor newfd, InetSocketAddress[] isaa) throws IOException; |
|
418 |
|
419 static { |
|
420 Util.load(); // loads nio & net native libraries |
|
421 java.security.AccessController.doPrivileged( |
|
422 new sun.security.action.LoadLibraryAction("sctp")); |
|
423 initIDs(); |
|
424 } |
|
425 } |
|