1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.transport.socket.nio.support;
21
22 import java.io.IOException;
23 import java.net.InetSocketAddress;
24 import java.net.SocketAddress;
25 import java.nio.channels.DatagramChannel;
26 import java.nio.channels.SelectionKey;
27 import java.nio.channels.Selector;
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34
35 import org.apache.mina.common.ByteBuffer;
36 import org.apache.mina.common.ExceptionMonitor;
37 import org.apache.mina.common.IoAcceptor;
38 import org.apache.mina.common.IoHandler;
39 import org.apache.mina.common.IoServiceConfig;
40 import org.apache.mina.common.IoSession;
41 import org.apache.mina.common.IoSessionRecycler;
42 import org.apache.mina.common.IoFilter.WriteRequest;
43 import org.apache.mina.common.support.BaseIoAcceptor;
44 import org.apache.mina.common.support.IoServiceListenerSupport;
45 import org.apache.mina.transport.socket.nio.DatagramAcceptorConfig;
46 import org.apache.mina.transport.socket.nio.DatagramServiceConfig;
47 import org.apache.mina.transport.socket.nio.DatagramSessionConfig;
48 import org.apache.mina.util.NamePreservingRunnable;
49 import org.apache.mina.util.Queue;
50
51 import edu.emory.mathcs.backport.java.util.concurrent.Executor;
52
53
54
55
56
57
58
59 public class DatagramAcceptorDelegate extends BaseIoAcceptor implements
60 IoAcceptor, DatagramService {
61 private static volatile int nextId = 0;
62
63 private final IoAcceptor wrapper;
64
65 private final Executor executor;
66
67 private final int id = nextId++;
68
69 private Selector selector;
70
71 private DatagramAcceptorConfig defaultConfig = new DatagramAcceptorConfig();
72
73 private final Map channels = new HashMap();
74
75 private final Queue registerQueue = new Queue();
76
77 private final Queue cancelQueue = new Queue();
78
79 private final Queue flushingSessions = new Queue();
80
81 private Worker worker;
82
83
84
85
86 public DatagramAcceptorDelegate(IoAcceptor wrapper, Executor executor) {
87 this.wrapper = wrapper;
88 this.executor = executor;
89
90
91 ((DatagramSessionConfig) defaultConfig.getSessionConfig())
92 .setReuseAddress(true);
93 }
94
95 public void bind(SocketAddress address, IoHandler handler,
96 IoServiceConfig config) throws IOException {
97 if (handler == null)
98 throw new NullPointerException("handler");
99 if (config == null) {
100 config = getDefaultConfig();
101 }
102
103 if (address != null && !(address instanceof InetSocketAddress))
104 throw new IllegalArgumentException("Unexpected address type: "
105 + address.getClass());
106
107 RegistrationRequest request = new RegistrationRequest(address, handler,
108 config);
109 synchronized (this) {
110 synchronized (registerQueue) {
111 registerQueue.push(request);
112 }
113 startupWorker();
114 selector.wakeup();
115 }
116
117 synchronized (request) {
118 while (!request.done) {
119 try {
120 request.wait();
121 } catch (InterruptedException e) {
122 }
123 }
124 }
125
126 if (request.exception != null) {
127 throw (IOException) new IOException("Failed to bind")
128 .initCause(request.exception);
129 }
130 }
131
132 public void unbind(SocketAddress address) {
133 if (address == null)
134 throw new NullPointerException("address");
135
136 CancellationRequest request = new CancellationRequest(address);
137 synchronized (this) {
138 try {
139 startupWorker();
140 } catch (IOException e) {
141
142
143
144
145 throw new IllegalArgumentException("Address not bound: "
146 + address);
147 }
148
149 synchronized (cancelQueue) {
150 cancelQueue.push(request);
151 }
152 selector.wakeup();
153 }
154
155 synchronized (request) {
156 while (!request.done) {
157 try {
158 request.wait();
159 } catch (InterruptedException e) {
160 }
161 }
162 }
163
164 if (request.exception != null) {
165 throw new RuntimeException("Failed to unbind", request.exception);
166 }
167 }
168
169 public void unbindAll() {
170 List addresses;
171 synchronized (channels) {
172 addresses = new ArrayList(channels.keySet());
173 }
174
175 for (Iterator i = addresses.iterator(); i.hasNext();) {
176 unbind((SocketAddress) i.next());
177 }
178 }
179
180 public IoSession newSession(SocketAddress remoteAddress,
181 SocketAddress localAddress) {
182 if (remoteAddress == null) {
183 throw new NullPointerException("remoteAddress");
184 }
185 if (localAddress == null) {
186 throw new NullPointerException("localAddress");
187 }
188
189 Selector selector = getSelector();
190 DatagramChannel ch = (DatagramChannel) channels.get(localAddress);
191 if (selector == null || ch == null) {
192 throw new IllegalArgumentException("Unknown localAddress: "
193 + localAddress);
194 }
195
196 SelectionKey key = ch.keyFor(selector);
197 if (key == null) {
198 throw new IllegalArgumentException("Unknown localAddress: "
199 + localAddress);
200 }
201
202 RegistrationRequest req = (RegistrationRequest) key.attachment();
203 IoSession session;
204 IoSessionRecycler sessionRecycler = getSessionRecycler(req);
205 synchronized (sessionRecycler) {
206 session = sessionRecycler.recycle(localAddress, remoteAddress);
207 if (session != null) {
208 return session;
209 }
210
211
212
213
214
215
216
217 DatagramSessionImpl datagramSession = new DatagramSessionImpl(
218 wrapper, this, req.config, ch, req.handler, req.address,
219 req.address);
220 datagramSession.setRemoteAddress(remoteAddress);
221 datagramSession.setSelectionKey(key);
222
223 getSessionRecycler(req).put(datagramSession);
224 session = datagramSession;
225 }
226
227 try {
228 buildFilterChain(req, session);
229 getListeners().fireSessionCreated(session);
230 } catch (Throwable t) {
231 ExceptionMonitor.getInstance().exceptionCaught(t);
232 }
233
234 return session;
235 }
236
237 private IoSessionRecycler getSessionRecycler(RegistrationRequest req) {
238 IoSessionRecycler sessionRecycler;
239 if (req.config instanceof DatagramServiceConfig) {
240 sessionRecycler = ((DatagramServiceConfig) req.config)
241 .getSessionRecycler();
242 } else {
243 sessionRecycler = defaultConfig.getSessionRecycler();
244 }
245 return sessionRecycler;
246 }
247
248 public IoServiceListenerSupport getListeners() {
249 return super.getListeners();
250 }
251
252 private void buildFilterChain(RegistrationRequest req, IoSession session)
253 throws Exception {
254 this.getFilterChainBuilder().buildFilterChain(session.getFilterChain());
255 req.config.getFilterChainBuilder().buildFilterChain(
256 session.getFilterChain());
257 req.config.getThreadModel().buildFilterChain(session.getFilterChain());
258 }
259
260 public IoServiceConfig getDefaultConfig() {
261 return defaultConfig;
262 }
263
264
265
266
267
268
269
270 public void setDefaultConfig(DatagramAcceptorConfig defaultConfig) {
271 if (defaultConfig == null) {
272 throw new NullPointerException("defaultConfig");
273 }
274 this.defaultConfig = defaultConfig;
275 }
276
277 private synchronized Selector getSelector() {
278 return this.selector;
279 }
280
281 private synchronized void startupWorker() throws IOException {
282 if (worker == null) {
283 selector = Selector.open();
284 worker = new Worker();
285 executor.execute(new NamePreservingRunnable(worker));
286 }
287 }
288
289 public void flushSession(DatagramSessionImpl session) {
290 scheduleFlush(session);
291 Selector selector = getSelector();
292 if (selector != null) {
293 selector.wakeup();
294 }
295 }
296
297 public void closeSession(DatagramSessionImpl session) {
298 }
299
300 private void scheduleFlush(DatagramSessionImpl session) {
301 synchronized (flushingSessions) {
302 flushingSessions.push(session);
303 }
304 }
305
306 private class Worker implements Runnable {
307 public void run() {
308 Thread.currentThread().setName("DatagramAcceptor-" + id);
309
310 Selector selector = DatagramAcceptorDelegate.this.getSelector();
311 for (;;) {
312 try {
313 int nKeys = selector.select();
314
315 registerNew();
316
317 if (nKeys > 0) {
318 processReadySessions(selector.selectedKeys());
319 }
320
321 flushSessions();
322 cancelKeys();
323
324 if (selector.keys().isEmpty()) {
325 synchronized (DatagramAcceptorDelegate.this) {
326 if (selector.keys().isEmpty()
327 && registerQueue.isEmpty()
328 && cancelQueue.isEmpty()) {
329 worker = null;
330 try {
331 selector.close();
332 } catch (IOException e) {
333 ExceptionMonitor.getInstance()
334 .exceptionCaught(e);
335 } finally {
336 DatagramAcceptorDelegate.this.selector = null;
337 }
338 break;
339 }
340 }
341 }
342 } catch (IOException e) {
343 ExceptionMonitor.getInstance().exceptionCaught(e);
344
345 try {
346 Thread.sleep(1000);
347 } catch (InterruptedException e1) {
348 }
349 }
350 }
351 }
352 }
353
354 private void processReadySessions(Set keys) {
355 Iterator it = keys.iterator();
356 while (it.hasNext()) {
357 SelectionKey key = (SelectionKey) it.next();
358 it.remove();
359
360 DatagramChannel ch = (DatagramChannel) key.channel();
361
362 RegistrationRequest req = (RegistrationRequest) key.attachment();
363 try {
364 if (key.isReadable()) {
365 readSession(ch, req);
366 }
367
368 if (key.isWritable()) {
369 for (Iterator i = getManagedSessions(req.address)
370 .iterator(); i.hasNext();) {
371 scheduleFlush((DatagramSessionImpl) i.next());
372 }
373 }
374 } catch (Throwable t) {
375 ExceptionMonitor.getInstance().exceptionCaught(t);
376 }
377 }
378 }
379
380 private void readSession(DatagramChannel channel, RegistrationRequest req)
381 throws Exception {
382 ByteBuffer readBuf = ByteBuffer
383 .allocate(((DatagramSessionConfig) req.config
384 .getSessionConfig()).getReceiveBufferSize());
385 try {
386 SocketAddress remoteAddress = channel.receive(readBuf.buf());
387 if (remoteAddress != null) {
388 DatagramSessionImpl session = (DatagramSessionImpl) newSession(
389 remoteAddress, req.address);
390
391 readBuf.flip();
392
393 ByteBuffer newBuf = ByteBuffer.allocate(readBuf.limit());
394 newBuf.put(readBuf);
395 newBuf.flip();
396
397 session.increaseReadBytes(newBuf.remaining());
398 session.getFilterChain().fireMessageReceived(session, newBuf);
399 }
400 } finally {
401 readBuf.release();
402 }
403 }
404
405 private void flushSessions() {
406 if (flushingSessions.size() == 0)
407 return;
408
409 for (;;) {
410 DatagramSessionImpl session;
411
412 synchronized (flushingSessions) {
413 session = (DatagramSessionImpl) flushingSessions.pop();
414 }
415
416 if (session == null)
417 break;
418
419 try {
420 flush(session);
421 } catch (IOException e) {
422 session.getFilterChain().fireExceptionCaught(session, e);
423 }
424 }
425 }
426
427 private void flush(DatagramSessionImpl session) throws IOException {
428 DatagramChannel ch = session.getChannel();
429
430 Queue writeRequestQueue = session.getWriteRequestQueue();
431
432 WriteRequest req;
433 for (;;) {
434 synchronized (writeRequestQueue) {
435 req = (WriteRequest) writeRequestQueue.first();
436 }
437
438 if (req == null)
439 break;
440
441 ByteBuffer buf = (ByteBuffer) req.getMessage();
442 if (buf.remaining() == 0) {
443
444 synchronized (writeRequestQueue) {
445 writeRequestQueue.pop();
446 }
447
448 session.increaseWrittenMessages();
449 buf.reset();
450 ((DatagramFilterChain) session.getFilterChain())
451 .fireMessageSent(session, req);
452 continue;
453 }
454
455 SelectionKey key = session.getSelectionKey();
456 if (key == null) {
457 scheduleFlush(session);
458 break;
459 }
460 if (!key.isValid()) {
461 continue;
462 }
463
464 SocketAddress destination = req.getDestination();
465 if (destination == null) {
466 destination = session.getRemoteAddress();
467 }
468
469 int writtenBytes = ch.send(buf.buf(), destination);
470
471 if (writtenBytes == 0) {
472
473 key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
474 } else if (writtenBytes > 0) {
475 key.interestOps(key.interestOps() & (~SelectionKey.OP_WRITE));
476
477
478 synchronized (writeRequestQueue) {
479 writeRequestQueue.pop();
480 }
481
482 session.increaseWrittenBytes(writtenBytes);
483 session.increaseWrittenMessages();
484 buf.reset();
485 session.getFilterChain().fireMessageSent(session, req);
486 }
487 }
488 }
489
490 private void registerNew() {
491 if (registerQueue.isEmpty())
492 return;
493
494 Selector selector = getSelector();
495 for (;;) {
496 RegistrationRequest req;
497 synchronized (registerQueue) {
498 req = (RegistrationRequest) registerQueue.pop();
499 }
500
501 if (req == null)
502 break;
503
504 DatagramChannel ch = null;
505 try {
506 ch = DatagramChannel.open();
507 DatagramSessionConfig cfg;
508 if (req.config.getSessionConfig() instanceof DatagramSessionConfig) {
509 cfg = (DatagramSessionConfig) req.config.getSessionConfig();
510 } else {
511 cfg = (DatagramSessionConfig) getDefaultConfig()
512 .getSessionConfig();
513 }
514
515 ch.socket().setReuseAddress(cfg.isReuseAddress());
516 ch.socket().setBroadcast(cfg.isBroadcast());
517 ch.socket().setReceiveBufferSize(cfg.getReceiveBufferSize());
518 ch.socket().setSendBufferSize(cfg.getSendBufferSize());
519
520 if (ch.socket().getTrafficClass() != cfg.getTrafficClass()) {
521 ch.socket().setTrafficClass(cfg.getTrafficClass());
522 }
523
524 ch.configureBlocking(false);
525 ch.socket().bind(req.address);
526 if (req.address == null || req.address.getPort() == 0) {
527 req.address = (InetSocketAddress) ch.socket()
528 .getLocalSocketAddress();
529 }
530 ch.register(selector, SelectionKey.OP_READ, req);
531 synchronized (channels) {
532 channels.put(req.address, ch);
533 }
534
535 getListeners().fireServiceActivated(this, req.address,
536 req.handler, req.config);
537 } catch (Throwable t) {
538 req.exception = t;
539 } finally {
540 synchronized (req) {
541 req.done = true;
542 req.notify();
543 }
544
545 if (ch != null && req.exception != null) {
546 try {
547 ch.disconnect();
548 ch.close();
549 } catch (Throwable e) {
550 ExceptionMonitor.getInstance().exceptionCaught(e);
551 }
552 }
553 }
554 }
555 }
556
557 private void cancelKeys() {
558 if (cancelQueue.isEmpty())
559 return;
560
561 Selector selector = getSelector();
562 for (;;) {
563 CancellationRequest request;
564 synchronized (cancelQueue) {
565 request = (CancellationRequest) cancelQueue.pop();
566 }
567
568 if (request == null) {
569 break;
570 }
571
572 DatagramChannel ch;
573 synchronized (channels) {
574 ch = (DatagramChannel) channels.remove(request.address);
575 }
576
577
578 try {
579 if (ch == null) {
580 request.exception = new IllegalArgumentException(
581 "Address not bound: " + request.address);
582 } else {
583 SelectionKey key = ch.keyFor(selector);
584 request.registrationRequest = (RegistrationRequest) key
585 .attachment();
586 key.cancel();
587 selector.wakeup();
588 ch.disconnect();
589 ch.close();
590 }
591 } catch (Throwable t) {
592 ExceptionMonitor.getInstance().exceptionCaught(t);
593 } finally {
594 synchronized (request) {
595 request.done = true;
596 request.notify();
597 }
598
599 if (request.exception == null) {
600 getListeners().fireServiceDeactivated(this,
601 request.address,
602 request.registrationRequest.handler,
603 request.registrationRequest.config);
604 }
605 }
606 }
607 }
608
609 public void updateTrafficMask(DatagramSessionImpl session) {
610
611
612
613 }
614
615 private static class RegistrationRequest {
616 private InetSocketAddress address;
617
618 private final IoHandler handler;
619
620 private final IoServiceConfig config;
621
622 private Throwable exception;
623
624 private boolean done;
625
626 private RegistrationRequest(SocketAddress address, IoHandler handler,
627 IoServiceConfig config) {
628 this.address = (InetSocketAddress) address;
629 this.handler = handler;
630 this.config = config;
631 }
632 }
633
634 private static class CancellationRequest {
635 private final SocketAddress address;
636
637 private boolean done;
638
639 private RegistrationRequest registrationRequest;
640
641 private RuntimeException exception;
642
643 private CancellationRequest(SocketAddress address) {
644 this.address = address;
645 }
646 }
647 }