1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.mina.io.socket;
20
21 import java.io.IOException;
22 import java.nio.channels.CancelledKeyException;
23 import java.nio.channels.SelectionKey;
24 import java.nio.channels.Selector;
25 import java.nio.channels.SocketChannel;
26 import java.util.Iterator;
27 import java.util.Set;
28
29 import org.apache.mina.common.ByteBuffer;
30 import org.apache.mina.common.IdleStatus;
31 import org.apache.mina.common.SessionConfig;
32 import org.apache.mina.io.WriteTimeoutException;
33 import org.apache.mina.util.Queue;
34
35 /***
36 * Performs all I/O operations for sockets which is connected or bound.
37 * This class is used by MINA internally.
38 *
39 * @author The Apache Directory Project (dev@directory.apache.org)
40 * @version $Rev: 327113 $, $Date: 2005-10-21 15:59:15 +0900 $,
41 */
42 class SocketIoProcessor
43 {
44 private static final SocketIoProcessor instance;
45
46 static
47 {
48 SocketIoProcessor tmp;
49
50 try
51 {
52 tmp = new SocketIoProcessor();
53 }
54 catch( IOException e )
55 {
56 InternalError error = new InternalError(
57 "Failed to open selector." );
58 error.initCause( e );
59 throw error;
60 }
61
62 instance = tmp;
63 }
64
65 private final Selector selector;
66
67 private final Queue newSessions = new Queue();
68
69 private final Queue removingSessions = new Queue();
70
71 private final Queue flushingSessions = new Queue();
72
73 private final Queue readableSessions = new Queue();
74
75 private Worker worker;
76
77 private long lastIdleCheckTime = System.currentTimeMillis();
78
79 private SocketIoProcessor() throws IOException
80 {
81 selector = Selector.open();
82 }
83
84 static SocketIoProcessor getInstance()
85 {
86 return instance;
87 }
88
89 void addSession( SocketSession session )
90 {
91 synchronized( this )
92 {
93 synchronized( newSessions )
94 {
95 newSessions.push( session );
96 }
97 startupWorker();
98 }
99
100 selector.wakeup();
101 }
102
103 void removeSession( SocketSession session )
104 {
105 scheduleRemove( session );
106 startupWorker();
107 selector.wakeup();
108 }
109
110 private synchronized void startupWorker()
111 {
112 if( worker == null )
113 {
114 worker = new Worker();
115 worker.start();
116 }
117 }
118
119 void flushSession( SocketSession session )
120 {
121 scheduleFlush( session );
122 selector.wakeup();
123 }
124
125 void addReadableSession( SocketSession session )
126 {
127 synchronized( readableSessions )
128 {
129 readableSessions.push( session );
130 }
131 selector.wakeup();
132 }
133
134 private void addSessions()
135 {
136 if( newSessions.isEmpty() )
137 return;
138
139 SocketSession session;
140
141 for( ;; )
142 {
143 synchronized( newSessions )
144 {
145 session = ( SocketSession ) newSessions.pop();
146 }
147
148 if( session == null )
149 break;
150
151 SocketChannel ch = session.getChannel();
152 boolean registered;
153
154 try
155 {
156 ch.configureBlocking( false );
157 session.setSelectionKey( ch.register( selector,
158 SelectionKey.OP_READ,
159 session ) );
160 registered = true;
161 }
162 catch( IOException e )
163 {
164 registered = false;
165 session.getManagerFilterChain().exceptionCaught( session, e );
166 }
167
168 if( registered )
169 {
170 session.getManagerFilterChain().sessionOpened( session );
171 }
172 }
173 }
174
175 private void removeSessions()
176 {
177 if( removingSessions.isEmpty() )
178 return;
179
180 for( ;; )
181 {
182 SocketSession session;
183
184 synchronized( removingSessions )
185 {
186 session = ( SocketSession ) removingSessions.pop();
187 }
188
189 if( session == null )
190 break;
191
192 SocketChannel ch = session.getChannel();
193 SelectionKey key = session.getSelectionKey();
194
195
196 if( key == null )
197 {
198 scheduleRemove( session );
199 break;
200 }
201
202
203 if( !key.isValid() )
204 {
205 continue;
206 }
207
208 try
209 {
210 key.cancel();
211 ch.close();
212 }
213 catch( IOException e )
214 {
215 session.getManagerFilterChain().exceptionCaught( session, e );
216 }
217 finally
218 {
219 releaseWriteBuffers( session );
220
221 session.getManagerFilterChain().sessionClosed( session );
222 session.notifyClose();
223 }
224 }
225 }
226
227 private void processSessions( Set selectedKeys )
228 {
229 Iterator it = selectedKeys.iterator();
230
231 while( it.hasNext() )
232 {
233 SelectionKey key = ( SelectionKey ) it.next();
234 SocketSession session = ( SocketSession ) key.attachment();
235
236 if( key.isReadable() )
237 {
238 read( session );
239 }
240
241 if( key.isWritable() )
242 {
243 scheduleFlush( session );
244 }
245 }
246
247 selectedKeys.clear();
248 }
249
250 private void read( SocketSession session )
251 {
252 ByteBuffer buf = ByteBuffer.allocate(
253 (( SocketSessionConfig ) session.getConfig()).getSessionReceiveBufferSize() );
254 SocketChannel ch = session.getChannel();
255
256 try
257 {
258 int readBytes = 0;
259 int ret;
260
261 buf.clear();
262
263 try
264 {
265 while( ( ret = ch.read( buf.buf() ) ) > 0 )
266 {
267 readBytes += ret;
268 }
269 }
270 finally
271 {
272 buf.flip();
273 }
274
275 session.increaseReadBytes( readBytes );
276 session.resetIdleCount( IdleStatus.BOTH_IDLE );
277 session.resetIdleCount( IdleStatus.READER_IDLE );
278
279 if( readBytes > 0 )
280 {
281 ByteBuffer newBuf = ByteBuffer.allocate( readBytes );
282 newBuf.put( buf );
283 newBuf.flip();
284 session.getManagerFilterChain().dataRead( session, newBuf );
285 }
286 if( ret < 0 )
287 {
288 scheduleRemove( session );
289 }
290 }
291 catch( Throwable e )
292 {
293 if( e instanceof IOException )
294 scheduleRemove( session );
295 session.getManagerFilterChain().exceptionCaught( session, e );
296 }
297 finally
298 {
299 buf.release();
300 }
301 }
302
303 private void scheduleRemove( SocketSession session )
304 {
305 synchronized( removingSessions )
306 {
307 removingSessions.push( session );
308 }
309 }
310
311 private void scheduleFlush( SocketSession session )
312 {
313 synchronized( flushingSessions )
314 {
315 flushingSessions.push( session );
316 }
317 }
318
319 private void notifyIdleSessions()
320 {
321 Set keys = selector.keys();
322 Iterator it;
323 SocketSession session;
324
325
326 long currentTime = System.currentTimeMillis();
327
328 if( ( keys != null ) && ( ( currentTime - lastIdleCheckTime ) >= 1000 ) )
329 {
330 lastIdleCheckTime = currentTime;
331 it = keys.iterator();
332
333 while( it.hasNext() )
334 {
335 SelectionKey key = ( SelectionKey ) it.next();
336 session = ( SocketSession ) key.attachment();
337
338 notifyIdleSession( session, currentTime );
339 }
340 }
341 }
342
343 private void notifyIdleSession( SocketSession session, long currentTime )
344 {
345 SessionConfig config = session.getConfig();
346
347 notifyIdleSession0(
348 session, currentTime,
349 config.getIdleTimeInMillis( IdleStatus.BOTH_IDLE ),
350 IdleStatus.BOTH_IDLE,
351 Math.max( session.getLastIoTime(), session.getLastIdleTime( IdleStatus.BOTH_IDLE ) ) );
352 notifyIdleSession0(
353 session, currentTime,
354 config.getIdleTimeInMillis( IdleStatus.READER_IDLE ),
355 IdleStatus.READER_IDLE,
356 Math.max( session.getLastReadTime(), session.getLastIdleTime( IdleStatus.READER_IDLE ) ) );
357 notifyIdleSession0(
358 session, currentTime,
359 config.getIdleTimeInMillis( IdleStatus.WRITER_IDLE ),
360 IdleStatus.WRITER_IDLE,
361 Math.max( session.getLastWriteTime(), session.getLastIdleTime( IdleStatus.WRITER_IDLE ) ) );
362
363 notifyWriteTimeoutSession( session, currentTime, config
364 .getWriteTimeoutInMillis(), session.getLastWriteTime() );
365 }
366
367 private void notifyIdleSession0( SocketSession session, long currentTime,
368 long idleTime, IdleStatus status,
369 long lastIoTime )
370 {
371 if( idleTime > 0 && lastIoTime != 0
372 && ( currentTime - lastIoTime ) >= idleTime )
373 {
374 session.increaseIdleCount( status );
375 session.getManagerFilterChain().sessionIdle( session, status );
376 }
377 }
378
379 private void notifyWriteTimeoutSession( SocketSession session,
380 long currentTime,
381 long writeTimeout, long lastIoTime )
382 {
383 if( writeTimeout > 0
384 && ( currentTime - lastIoTime ) >= writeTimeout
385 && session.getSelectionKey() != null
386 && ( session.getSelectionKey().interestOps() & SelectionKey.OP_WRITE ) != 0 )
387 {
388 session
389 .getManagerFilterChain()
390 .exceptionCaught( session, new WriteTimeoutException() );
391 }
392 }
393
394 private void flushSessions()
395 {
396 if( flushingSessions.size() == 0 )
397 return;
398
399 for( ;; )
400 {
401 SocketSession session;
402
403 synchronized( flushingSessions )
404 {
405 session = ( SocketSession ) flushingSessions.pop();
406 }
407
408 if( session == null )
409 break;
410
411 if( !session.isConnected() )
412 {
413 releaseWriteBuffers( session );
414 continue;
415 }
416
417
418
419 if( session.getSelectionKey() == null )
420 {
421
422 scheduleFlush( session );
423 break;
424 }
425 else
426 {
427 try
428 {
429 flush( session );
430 }
431 catch( CancelledKeyException e )
432 {
433
434 scheduleRemove( session );
435 }
436 catch( IOException e )
437 {
438 scheduleRemove( session );
439 session.getManagerFilterChain().exceptionCaught( session, e );
440 }
441 }
442 }
443 }
444
445 private void releaseWriteBuffers( SocketSession session )
446 {
447 Queue writeBufferQueue = session.getWriteBufferQueue();
448 session.getWriteMarkerQueue().clear();
449 ByteBuffer buf;
450
451 while( ( buf = (ByteBuffer) writeBufferQueue.pop() ) != null )
452 {
453 try
454 {
455 buf.release();
456 }
457 catch( IllegalStateException e )
458 {
459 session.getManagerFilterChain().exceptionCaught( session, e );
460 }
461 }
462 }
463
464 private void flush( SocketSession session ) throws IOException
465 {
466 SocketChannel ch = session.getChannel();
467
468 Queue writeBufferQueue = session.getWriteBufferQueue();
469 Queue writeMarkerQueue = session.getWriteMarkerQueue();
470
471 ByteBuffer buf;
472 Object marker;
473 for( ;; )
474 {
475 synchronized( writeBufferQueue )
476 {
477 buf = ( ByteBuffer ) writeBufferQueue.first();
478 marker = writeMarkerQueue.first();
479 }
480
481 if( buf == null )
482 break;
483
484 if( buf.remaining() == 0 )
485 {
486 synchronized( writeBufferQueue )
487 {
488 writeBufferQueue.pop();
489 writeMarkerQueue.pop();
490 }
491 try
492 {
493 buf.release();
494 }
495 catch( IllegalStateException e )
496 {
497 session.getManagerFilterChain().exceptionCaught( session, e );
498 }
499
500 session.increaseWrittenWriteRequests();
501 session.getManagerFilterChain().dataWritten( session, marker );
502 continue;
503 }
504
505 int writtenBytes = 0;
506 try
507 {
508 writtenBytes = ch.write( buf.buf() );
509 }
510 finally
511 {
512 if( writtenBytes > 0 )
513 {
514 session.increaseWrittenBytes( writtenBytes );
515 session.resetIdleCount( IdleStatus.BOTH_IDLE );
516 session.resetIdleCount( IdleStatus.WRITER_IDLE );
517 }
518
519 SelectionKey key = session.getSelectionKey();
520 if( buf.hasRemaining() )
521 {
522
523 key
524 .interestOps( key.interestOps()
525 | SelectionKey.OP_WRITE );
526 break;
527 }
528 else
529 {
530 key.interestOps( key.interestOps()
531 & ( ~SelectionKey.OP_WRITE ) );
532 }
533 }
534 }
535 }
536
537 private class Worker extends Thread
538 {
539 public Worker()
540 {
541 super( "SocketIoProcessor" );
542 }
543
544 public void run()
545 {
546 for( ;; )
547 {
548 try
549 {
550 int nKeys = selector.select( 1000 );
551 addSessions();
552
553 if( nKeys > 0 )
554 {
555 processSessions( selector.selectedKeys() );
556 }
557
558 flushSessions();
559 removeSessions();
560 notifyIdleSessions();
561
562 if( selector.keys().isEmpty() )
563 {
564 synchronized( SocketIoProcessor.this )
565 {
566 if( selector.keys().isEmpty() &&
567 newSessions.isEmpty() )
568 {
569 worker = null;
570 break;
571 }
572 }
573 }
574 }
575 catch( IOException e )
576 {
577 e.printStackTrace();
578
579 try
580 {
581 Thread.sleep( 1000 );
582 }
583 catch( InterruptedException e1 )
584 {
585 }
586 }
587 }
588 }
589 }
590 }