1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.common;
21
22 import java.net.SocketAddress;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Queue;
30 import java.util.Set;
31 import java.util.concurrent.ConcurrentLinkedQueue;
32 import java.util.concurrent.Executor;
33 import java.util.concurrent.ExecutorService;
34 import java.util.concurrent.LinkedBlockingQueue;
35 import java.util.concurrent.RejectedExecutionException;
36 import java.util.concurrent.ThreadPoolExecutor;
37 import java.util.concurrent.TimeUnit;
38 import java.util.concurrent.atomic.AtomicInteger;
39
40 import org.apache.mina.util.NamePreservingRunnable;
41
42
43
44
45
46
47
48 public abstract class AbstractPollingConnectionlessIoAcceptor<T extends AbstractIoSession, H>
49 extends AbstractIoAcceptor {
50
51 private static final IoSessionRecycler DEFAULT_RECYCLER = new ExpiringSessionRecycler();
52
53 private static final AtomicInteger id = new AtomicInteger();
54
55 private final Object lock = new Object();
56 private final Executor executor;
57 private final boolean createdExecutor;
58 private final String threadName;
59 private final IoProcessor<T> processor = new ConnectionlessAcceptorProcessor();
60 private final Queue<AcceptorOperationFuture> registerQueue =
61 new ConcurrentLinkedQueue<AcceptorOperationFuture>();
62 private final Queue<AcceptorOperationFuture> cancelQueue =
63 new ConcurrentLinkedQueue<AcceptorOperationFuture>();
64 private final Queue<T> flushingSessions = new ConcurrentLinkedQueue<T>();
65 private final Map<SocketAddress, H> boundHandles =
66 Collections.synchronizedMap(new HashMap<SocketAddress, H>());
67
68 private IoSessionRecycler sessionRecycler = DEFAULT_RECYCLER;
69
70 private final ServiceOperationFuture disposalFuture =
71 new ServiceOperationFuture();
72 private volatile boolean selectable;
73 private Worker worker;
74 private long lastIdleCheckTime;
75
76
77
78
79 protected AbstractPollingConnectionlessIoAcceptor(IoSessionConfig sessionConfig) {
80 this(sessionConfig, null);
81 }
82
83
84
85
86 protected AbstractPollingConnectionlessIoAcceptor(IoSessionConfig sessionConfig, Executor executor) {
87 super(sessionConfig);
88
89 threadName = getClass().getSimpleName() + '-' + id.incrementAndGet();
90
91 if (executor == null) {
92 this.executor = new ThreadPoolExecutor(
93 1, 1, 1L, TimeUnit.SECONDS,
94 new LinkedBlockingQueue<Runnable>());
95 this.createdExecutor = true;
96 } else {
97 this.executor = executor;
98 this.createdExecutor = false;
99 }
100
101 try {
102 init();
103 selectable = true;
104 } catch (RuntimeException e){
105 throw e;
106 } catch (Exception e) {
107 throw new RuntimeIoException("Failed to initialize.", e);
108 } finally {
109 if (!selectable) {
110 try {
111 destroy();
112 } catch (Exception e) {
113 ExceptionMonitor.getInstance().exceptionCaught(e);
114 }
115 }
116 }
117 }
118
119 protected abstract void init() throws Exception;
120 protected abstract void destroy() throws Exception;
121 protected abstract boolean select(int timeout) throws Exception;
122 protected abstract void wakeup();
123 protected abstract Iterator<H> selectedHandles();
124 protected abstract H open(SocketAddress localAddress) throws Exception;
125 protected abstract void close(H handle) throws Exception;
126 protected abstract SocketAddress localAddress(H handle) throws Exception;
127 protected abstract boolean isReadable(H handle);
128 protected abstract boolean isWritable(H handle);
129 protected abstract SocketAddress receive(H handle, IoBuffer buffer) throws Exception;
130 protected abstract int send(T session, IoBuffer buffer, SocketAddress remoteAddress) throws Exception;
131 protected abstract T newSession(IoProcessor<T> processor, H handle, SocketAddress remoteAddress) throws Exception;
132 protected abstract void setInterestedInWrite(T session, boolean interested) throws Exception;
133
134 @Override
135 protected IoFuture dispose0() throws Exception {
136 unbind();
137 if (!disposalFuture.isDone()) {
138 try {
139 startupWorker();
140 wakeup();
141 } catch (RejectedExecutionException e) {
142 if (createdExecutor) {
143
144 } else {
145 throw e;
146 }
147 }
148 }
149 return disposalFuture;
150 }
151
152 @Override
153 protected final Set<SocketAddress> bind0(
154 List<? extends SocketAddress> localAddresses) throws Exception {
155 AcceptorOperationFuture request = new AcceptorOperationFuture(localAddresses);
156
157 registerQueue.add(request);
158 startupWorker();
159 wakeup();
160
161 request.awaitUninterruptibly();
162
163 if (request.getException() != null) {
164 throw request.getException();
165 }
166
167 Set<SocketAddress> newLocalAddresses = new HashSet<SocketAddress>();
168 for (H handle: boundHandles.values()) {
169 newLocalAddresses.add(localAddress(handle));
170 }
171 return newLocalAddresses;
172 }
173
174 @Override
175 protected final void unbind0(
176 List<? extends SocketAddress> localAddresses) throws Exception {
177 AcceptorOperationFuture request = new AcceptorOperationFuture(localAddresses);
178
179 cancelQueue.add(request);
180 startupWorker();
181 wakeup();
182
183 request.awaitUninterruptibly();
184
185 if (request.getException() != null) {
186 throw request.getException();
187 }
188 }
189
190 public final IoSession newSession(SocketAddress remoteAddress, SocketAddress localAddress) {
191 if (isDisposing()) {
192 throw new IllegalStateException("Already disposed.");
193 }
194
195 if (remoteAddress == null) {
196 throw new NullPointerException("remoteAddress");
197 }
198
199 synchronized (bindLock) {
200 if (!isActive()) {
201 throw new IllegalStateException(
202 "Can't create a session from a unbound service.");
203 }
204
205 try {
206 return newSessionWithoutLock(remoteAddress, localAddress);
207 } catch (RuntimeException e) {
208 throw e;
209 } catch (Error e) {
210 throw e;
211 } catch (Exception e) {
212 throw new RuntimeIoException("Failed to create a session.", e);
213 }
214 }
215 }
216
217 private IoSession newSessionWithoutLock(
218 SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
219 H handle = boundHandles.get(localAddress);
220 if (handle == null) {
221 throw new IllegalArgumentException("Unknown local address: " + localAddress);
222 }
223
224 IoSession session;
225 IoSessionRecycler sessionRecycler = getSessionRecycler();
226 synchronized (sessionRecycler) {
227 session = sessionRecycler.recycle(localAddress, remoteAddress);
228 if (session != null) {
229 return session;
230 }
231
232
233 T newSession = newSession(processor, handle, remoteAddress);
234 getSessionRecycler().put(newSession);
235 session = newSession;
236 }
237
238 finishSessionInitialization(session, null, null);
239
240 try {
241 this.getFilterChainBuilder().buildFilterChain(session.getFilterChain());
242 getListeners().fireSessionCreated(session);
243 } catch (Throwable t) {
244 ExceptionMonitor.getInstance().exceptionCaught(t);
245 }
246
247 return session;
248 }
249
250 public final IoSessionRecycler getSessionRecycler() {
251 return sessionRecycler;
252 }
253
254 public final void setSessionRecycler(IoSessionRecycler sessionRecycler) {
255 synchronized (bindLock) {
256 if (isActive()) {
257 throw new IllegalStateException(
258 "sessionRecycler can't be set while the acceptor is bound.");
259 }
260
261 if (sessionRecycler == null) {
262 sessionRecycler = DEFAULT_RECYCLER;
263 }
264 this.sessionRecycler = sessionRecycler;
265 }
266 }
267
268 private class ConnectionlessAcceptorProcessor implements IoProcessor<T> {
269
270 public void add(T session) {
271 }
272
273 public void flush(T session) {
274 if (scheduleFlush(session)) {
275 wakeup();
276 }
277 }
278
279 public void remove(T session) {
280 getSessionRecycler().remove(session);
281 getListeners().fireSessionDestroyed(session);
282 }
283
284 public void updateTrafficMask(T session) {
285 throw new UnsupportedOperationException();
286 }
287
288 public void dispose() {
289 }
290
291 public boolean isDisposed() {
292 return false;
293 }
294
295 public boolean isDisposing() {
296 return false;
297 }
298 }
299
300 private void startupWorker() {
301 if (!selectable) {
302 registerQueue.clear();
303 cancelQueue.clear();
304 flushingSessions.clear();
305 }
306
307 synchronized (lock) {
308 if (worker == null) {
309 worker = new Worker();
310 executor.execute(
311 new NamePreservingRunnable(worker, threadName));
312 }
313 }
314 }
315
316 private boolean scheduleFlush(T session) {
317 if (session.setScheduledForFlush(true)) {
318 flushingSessions.add(session);
319 return true;
320 } else {
321 return false;
322 }
323 }
324
325 private class Worker implements Runnable {
326 public void run() {
327 int nHandles = 0;
328 lastIdleCheckTime = System.currentTimeMillis();
329
330 while (selectable) {
331 try {
332 boolean selected = select(1000);
333
334 nHandles += registerHandles();
335
336 if (selected) {
337 processReadySessions(selectedHandles());
338 }
339
340 flushSessions();
341 nHandles -= unregisterHandles();
342
343 notifyIdleSessions();
344
345 if (nHandles == 0) {
346 synchronized (lock) {
347 if (registerQueue.isEmpty() && cancelQueue.isEmpty()) {
348 worker = null;
349 break;
350 }
351 }
352 }
353 } catch (Exception e) {
354 ExceptionMonitor.getInstance().exceptionCaught(e);
355
356 try {
357 Thread.sleep(1000);
358 } catch (InterruptedException e1) {
359 }
360 }
361 }
362
363 if (selectable && isDisposing()) {
364 selectable = false;
365 try {
366 destroy();
367 } catch (Exception e) {
368 ExceptionMonitor.getInstance().exceptionCaught(e);
369 } finally {
370 disposalFuture.setValue(true);
371 if (createdExecutor) {
372 ((ExecutorService) executor).shutdown();
373 }
374 }
375 }
376 }
377 }
378
379 @SuppressWarnings("unchecked")
380 private void processReadySessions(Iterator<H> handles) {
381 while (handles.hasNext()) {
382 H h = handles.next();
383 handles.remove();
384 try {
385 if (isReadable(h)) {
386 readHandle(h);
387 }
388
389 if (isWritable(h)) {
390 for (IoSession session : getManagedSessions()) {
391 scheduleFlush((T) session);
392 }
393 }
394 } catch (Throwable t) {
395 ExceptionMonitor.getInstance().exceptionCaught(t);
396 }
397 }
398 }
399
400 private void readHandle(H handle) throws Exception {
401 IoBuffer readBuf = IoBuffer.allocate(
402 getSessionConfig().getReadBufferSize());
403
404 SocketAddress remoteAddress = receive(handle, readBuf);
405 if (remoteAddress != null) {
406 IoSession session = newSessionWithoutLock(
407 remoteAddress, localAddress(handle));
408
409 readBuf.flip();
410
411 IoBuffer newBuf = IoBuffer.allocate(readBuf.limit());
412 newBuf.put(readBuf);
413 newBuf.flip();
414
415 session.getFilterChain().fireMessageReceived(newBuf);
416 }
417 }
418
419 private void flushSessions() {
420 for (; ;) {
421 T session = flushingSessions.poll();
422 if (session == null) {
423 break;
424 }
425
426 session.setScheduledForFlush(false);
427
428 try {
429 boolean flushedAll = flush(session);
430 if (flushedAll && !session.getWriteRequestQueue().isEmpty(session) &&
431 !session.isScheduledForFlush()) {
432 scheduleFlush(session);
433 }
434 } catch (Exception e) {
435 session.getFilterChain().fireExceptionCaught(e);
436 }
437 }
438 }
439
440 private boolean flush(T session) throws Exception {
441
442 setInterestedInWrite(session, false);
443
444 WriteRequestQueue writeRequestQueue = session.getWriteRequestQueue();
445
446 int maxWrittenBytes =
447 session.getConfig().getMaxReadBufferSize() +
448 (session.getConfig().getMaxReadBufferSize() >>> 1);
449
450 int writtenBytes = 0;
451 for (; ;) {
452 WriteRequest req = session.getCurrentWriteRequest();
453 if (req == null) {
454 req = writeRequestQueue.poll(session);
455 if (req == null) {
456 break;
457 }
458 session.setCurrentWriteRequest(req);
459 }
460
461 IoBuffer buf = (IoBuffer) req.getMessage();
462 if (buf.remaining() == 0) {
463
464 session.setCurrentWriteRequest(null);
465 buf.reset();
466 session.getFilterChain().fireMessageSent(req);
467 continue;
468 }
469
470 SocketAddress destination = req.getDestination();
471 if (destination == null) {
472 destination = session.getRemoteAddress();
473 }
474
475 int localWrittenBytes = send(session, buf, destination);
476 if (localWrittenBytes == 0 || writtenBytes >= maxWrittenBytes) {
477
478 setInterestedInWrite(session, true);
479 return false;
480 } else {
481 setInterestedInWrite(session, false);
482
483
484 session.setCurrentWriteRequest(null);
485 writtenBytes += localWrittenBytes;
486 buf.reset();
487 session.getFilterChain().fireMessageSent(req);
488 }
489 }
490
491 return true;
492 }
493
494 private int registerHandles() {
495 for (;;) {
496 AcceptorOperationFuture req = registerQueue.poll();
497 if (req == null) {
498 break;
499 }
500
501 Map<SocketAddress, H> newHandles = new HashMap<SocketAddress, H>();
502 List<SocketAddress> localAddresses = req.getLocalAddresses();
503 try {
504 for (SocketAddress a: localAddresses) {
505 H handle = open(a);
506 newHandles.put(localAddress(handle), handle);
507 }
508 boundHandles.putAll(newHandles);
509
510 getListeners().fireServiceActivated();
511 req.setDone();
512 return newHandles.size();
513 } catch (Exception e) {
514 req.setException(e);
515 } finally {
516
517 if (req.getException() != null) {
518 for (H handle: newHandles.values()) {
519 try {
520 close(handle);
521 } catch (Exception e) {
522 ExceptionMonitor.getInstance().exceptionCaught(e);
523 }
524 }
525 wakeup();
526 }
527 }
528 }
529
530 return 0;
531 }
532
533 private int unregisterHandles() {
534 int nHandles = 0;
535 for (;;) {
536 AcceptorOperationFuture request = cancelQueue.poll();
537 if (request == null) {
538 break;
539 }
540
541
542 for (SocketAddress a: request.getLocalAddresses()) {
543 H handle = boundHandles.remove(a);
544 if (handle == null) {
545 continue;
546 }
547
548 try {
549 close(handle);
550 wakeup();
551 } catch (Throwable e) {
552 ExceptionMonitor.getInstance().exceptionCaught(e);
553 } finally {
554 nHandles ++;
555 }
556 }
557
558 request.setDone();
559 }
560
561 return nHandles;
562 }
563
564 private void notifyIdleSessions() {
565
566 long currentTime = System.currentTimeMillis();
567 if (currentTime - lastIdleCheckTime >= 1000) {
568 lastIdleCheckTime = currentTime;
569 IdleStatusChecker.notifyIdleness(
570 getListeners().getManagedSessions().iterator(),
571 currentTime);
572 }
573 }
574 }