1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.core.polling;
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
34 import org.apache.mina.core.RuntimeIoException;
35 import org.apache.mina.core.buffer.IoBuffer;
36 import org.apache.mina.core.future.IoFuture;
37 import org.apache.mina.core.service.AbstractIoAcceptor;
38 import org.apache.mina.core.service.IoAcceptor;
39 import org.apache.mina.core.service.IoProcessor;
40 import org.apache.mina.core.session.AbstractIoSession;
41 import org.apache.mina.core.session.ExpiringSessionRecycler;
42 import org.apache.mina.core.session.IoSession;
43 import org.apache.mina.core.session.IoSessionConfig;
44 import org.apache.mina.core.session.IoSessionRecycler;
45 import org.apache.mina.core.write.WriteRequest;
46 import org.apache.mina.core.write.WriteRequestQueue;
47 import org.apache.mina.util.ExceptionMonitor;
48
49
50
51
52
53
54
55
56
57 public abstract class AbstractPollingConnectionlessIoAcceptor<T extends AbstractIoSession, H>
58 extends AbstractIoAcceptor {
59
60 private static final IoSessionRecycler DEFAULT_RECYCLER = new ExpiringSessionRecycler();
61
62 private final Object lock = new Object();
63 private final IoProcessor<T> processor = new ConnectionlessAcceptorProcessor();
64 private final Queue<AcceptorOperationFuture> registerQueue =
65 new ConcurrentLinkedQueue<AcceptorOperationFuture>();
66 private final Queue<AcceptorOperationFuture> cancelQueue =
67 new ConcurrentLinkedQueue<AcceptorOperationFuture>();
68 private final Queue<T> flushingSessions = new ConcurrentLinkedQueue<T>();
69 private final Map<SocketAddress, H> boundHandles =
70 Collections.synchronizedMap(new HashMap<SocketAddress, H>());
71
72 private IoSessionRecycler sessionRecycler = DEFAULT_RECYCLER;
73
74 private final ServiceOperationFuture disposalFuture =
75 new ServiceOperationFuture();
76 private volatile boolean selectable;
77
78
79 private Acceptor acceptor;
80
81 private long lastIdleCheckTime;
82
83
84
85
86 protected AbstractPollingConnectionlessIoAcceptor(IoSessionConfig sessionConfig) {
87 this(sessionConfig, null);
88 }
89
90
91
92
93 protected AbstractPollingConnectionlessIoAcceptor(IoSessionConfig sessionConfig, Executor executor) {
94 super(sessionConfig, executor);
95
96 try {
97 init();
98 selectable = true;
99 } catch (RuntimeException e){
100 throw e;
101 } catch (Exception e) {
102 throw new RuntimeIoException("Failed to initialize.", e);
103 } finally {
104 if (!selectable) {
105 try {
106 destroy();
107 } catch (Exception e) {
108 ExceptionMonitor.getInstance().exceptionCaught(e);
109 }
110 }
111 }
112 }
113
114 protected abstract void init() throws Exception;
115 protected abstract void destroy() throws Exception;
116 protected abstract int select() throws Exception;
117 protected abstract int select(int timeout) throws Exception;
118 protected abstract void wakeup();
119 protected abstract Iterator<H> selectedHandles();
120 protected abstract H open(SocketAddress localAddress) throws Exception;
121 protected abstract void close(H handle) throws Exception;
122 protected abstract SocketAddress localAddress(H handle) throws Exception;
123 protected abstract boolean isReadable(H handle);
124 protected abstract boolean isWritable(H handle);
125 protected abstract SocketAddress receive(H handle, IoBuffer buffer) throws Exception;
126 protected abstract int send(T session, IoBuffer buffer, SocketAddress remoteAddress) throws Exception;
127 protected abstract T newSession(IoProcessor<T> processor, H handle, SocketAddress remoteAddress) throws Exception;
128 protected abstract void setInterestedInWrite(T session, boolean interested) throws Exception;
129
130
131
132
133 @Override
134 protected IoFuture dispose0() throws Exception {
135 unbind();
136 if (!disposalFuture.isDone()) {
137 startupWorker();
138 wakeup();
139 }
140 return disposalFuture;
141 }
142
143
144
145
146 @Override
147 protected final Set<SocketAddress> bind0(
148 List<? extends SocketAddress> localAddresses) throws Exception {
149 AcceptorOperationFuture request = new AcceptorOperationFuture(localAddresses);
150
151 registerQueue.add(request);
152 startupWorker();
153 wakeup();
154
155 request.awaitUninterruptibly();
156
157 if (request.getException() != null) {
158 throw request.getException();
159 }
160
161 Set<SocketAddress> newLocalAddresses = new HashSet<SocketAddress>();
162 for (H handle: boundHandles.values()) {
163 newLocalAddresses.add(localAddress(handle));
164 }
165 return newLocalAddresses;
166 }
167
168
169
170
171 @Override
172 protected final void unbind0(
173 List<? extends SocketAddress> localAddresses) throws Exception {
174 AcceptorOperationFuture request = new AcceptorOperationFuture(localAddresses);
175
176 cancelQueue.add(request);
177 startupWorker();
178 wakeup();
179
180 request.awaitUninterruptibly();
181
182 if (request.getException() != null) {
183 throw request.getException();
184 }
185 }
186
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 updateTrafficControl(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 (acceptor == null) {
309 acceptor = new Acceptor();
310 executeWorker(acceptor);
311 }
312 }
313 }
314
315 private boolean scheduleFlush(T session) {
316 if (session.setScheduledForFlush(true)) {
317 flushingSessions.add(session);
318 return true;
319 } else {
320 return false;
321 }
322 }
323
324
325
326
327
328
329 private class Acceptor implements Runnable {
330 public void run() {
331 int nHandles = 0;
332 lastIdleCheckTime = System.currentTimeMillis();
333
334 while (selectable) {
335 try {
336 int selected = select();
337
338 nHandles += registerHandles();
339
340 if (selected > 0) {
341 processReadySessions(selectedHandles());
342 }
343
344 long currentTime = System.currentTimeMillis();
345 flushSessions(currentTime);
346 nHandles -= unregisterHandles();
347
348 notifyIdleSessions(currentTime);
349
350 if (nHandles == 0) {
351 synchronized (lock) {
352 if (registerQueue.isEmpty() && cancelQueue.isEmpty()) {
353 acceptor = null;
354 break;
355 }
356 }
357 }
358 } catch (Exception e) {
359 ExceptionMonitor.getInstance().exceptionCaught(e);
360
361 try {
362 Thread.sleep(1000);
363 } catch (InterruptedException e1) {
364 }
365 }
366 }
367
368 if (selectable && isDisposing()) {
369 selectable = false;
370 try {
371 destroy();
372 } catch (Exception e) {
373 ExceptionMonitor.getInstance().exceptionCaught(e);
374 } finally {
375 disposalFuture.setValue(true);
376 }
377 }
378 }
379 }
380
381 @SuppressWarnings("unchecked")
382 private void processReadySessions(Iterator<H> handles) {
383 while (handles.hasNext()) {
384 H h = handles.next();
385 handles.remove();
386 try {
387 if (isReadable(h)) {
388 readHandle(h);
389 }
390
391 if (isWritable(h)) {
392 for (IoSession session : getManagedSessions().values()) {
393 scheduleFlush((T) session);
394 }
395 }
396 } catch (Throwable t) {
397 ExceptionMonitor.getInstance().exceptionCaught(t);
398 }
399 }
400 }
401
402 private void readHandle(H handle) throws Exception {
403 IoBuffer readBuf = IoBuffer.allocate(
404 getSessionConfig().getReadBufferSize());
405
406 SocketAddress remoteAddress = receive(handle, readBuf);
407 if (remoteAddress != null) {
408 IoSession session = newSessionWithoutLock(
409 remoteAddress, localAddress(handle));
410
411 readBuf.flip();
412
413 IoBuffer newBuf = IoBuffer.allocate(readBuf.limit());
414 newBuf.put(readBuf);
415 newBuf.flip();
416
417 session.getFilterChain().fireMessageReceived(newBuf);
418 }
419 }
420
421 private void flushSessions(long currentTime) {
422 for (; ;) {
423 T session = flushingSessions.poll();
424 if (session == null) {
425 break;
426 }
427
428 session.setScheduledForFlush(false);
429
430 try {
431 boolean flushedAll = flush(session, currentTime);
432 if (flushedAll && !session.getWriteRequestQueue().isEmpty(session) &&
433 !session.isScheduledForFlush()) {
434 scheduleFlush(session);
435 }
436 } catch (Exception e) {
437 session.getFilterChain().fireExceptionCaught(e);
438 }
439 }
440 }
441
442 private boolean flush(T session, long currentTime) throws Exception {
443
444 setInterestedInWrite(session, false);
445
446 final WriteRequestQueue writeRequestQueue = session.getWriteRequestQueue();
447 final int maxWrittenBytes =
448 session.getConfig().getMaxReadBufferSize() +
449 (session.getConfig().getMaxReadBufferSize() >>> 1);
450
451 int writtenBytes = 0;
452 try {
453 for (; ;) {
454 WriteRequest req = session.getCurrentWriteRequest();
455 if (req == null) {
456 req = writeRequestQueue.poll(session);
457 if (req == null) {
458 break;
459 }
460 session.setCurrentWriteRequest(req);
461 }
462
463 IoBuffer buf = (IoBuffer) req.getMessage();
464 if (buf.remaining() == 0) {
465
466 session.setCurrentWriteRequest(null);
467 buf.reset();
468 session.getFilterChain().fireMessageSent(req);
469 continue;
470 }
471
472 SocketAddress destination = req.getDestination();
473 if (destination == null) {
474 destination = session.getRemoteAddress();
475 }
476
477 int localWrittenBytes = send(session, buf, destination);
478 if (localWrittenBytes == 0 || writtenBytes >= maxWrittenBytes) {
479
480 setInterestedInWrite(session, true);
481 return false;
482 } else {
483 setInterestedInWrite(session, false);
484
485
486 session.setCurrentWriteRequest(null);
487 writtenBytes += localWrittenBytes;
488 buf.reset();
489 session.getFilterChain().fireMessageSent(req);
490 }
491 }
492 } finally {
493 session.increaseWrittenBytes(writtenBytes, currentTime);
494 }
495
496 return true;
497 }
498
499 private int registerHandles() {
500 for (;;) {
501 AcceptorOperationFuture req = registerQueue.poll();
502 if (req == null) {
503 break;
504 }
505
506 Map<SocketAddress, H> newHandles = new HashMap<SocketAddress, H>();
507 List<SocketAddress> localAddresses = req.getLocalAddresses();
508 try {
509 for (SocketAddress a: localAddresses) {
510 H handle = open(a);
511 newHandles.put(localAddress(handle), handle);
512 }
513 boundHandles.putAll(newHandles);
514
515 getListeners().fireServiceActivated();
516 req.setDone();
517 return newHandles.size();
518 } catch (Exception e) {
519 req.setException(e);
520 } finally {
521
522 if (req.getException() != null) {
523 for (H handle: newHandles.values()) {
524 try {
525 close(handle);
526 } catch (Exception e) {
527 ExceptionMonitor.getInstance().exceptionCaught(e);
528 }
529 }
530 wakeup();
531 }
532 }
533 }
534
535 return 0;
536 }
537
538 private int unregisterHandles() {
539 int nHandles = 0;
540 for (;;) {
541 AcceptorOperationFuture request = cancelQueue.poll();
542 if (request == null) {
543 break;
544 }
545
546
547 for (SocketAddress a: request.getLocalAddresses()) {
548 H handle = boundHandles.remove(a);
549 if (handle == null) {
550 continue;
551 }
552
553 try {
554 close(handle);
555 wakeup();
556 } catch (Throwable e) {
557 ExceptionMonitor.getInstance().exceptionCaught(e);
558 } finally {
559 nHandles ++;
560 }
561 }
562
563 request.setDone();
564 }
565
566 return nHandles;
567 }
568
569 private void notifyIdleSessions(long currentTime) {
570
571 if (currentTime - lastIdleCheckTime >= 1000) {
572 lastIdleCheckTime = currentTime;
573 AbstractIoSession.notifyIdleness(
574 getListeners().getManagedSessions().values().iterator(),
575 currentTime);
576 }
577 }
578 }