EMMA Coverage Report (generated Fri Oct 21 16:16:13 KST 2005)
[all classes][org.apache.mina.io.socket]

COVERAGE SUMMARY FOR SOURCE FILE [SocketIoProcessor.java]

nameclass, %method, %block, %line, %
SocketIoProcessor.java100% (2/2)96%  (22/23)70%  (627/896)76%  (183.7/243)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class SocketIoProcessor100% (1/1)95%  (20/21)69%  (561/814)76%  (166.7/219)
addReadableSession (SocketSession): void 0%   (0/1)0%   (0/22)0%   (0/5)
notifyWriteTimeoutSession (SocketSession, long, long, long): void 100% (1/1)19%  (5/27)40%  (1.2/3)
<static initializer> 100% (1/1)40%  (8/20)50%  (4/8)
removeSessions (): void 100% (1/1)56%  (48/86)67%  (16.1/24)
flush (SocketSession): void 100% (1/1)60%  (87/146)76%  (26.6/35)
flushSessions (): void 100% (1/1)62%  (37/60)64%  (14.2/22)
notifyIdleSession0 (SocketSession, long, long, IdleStatus, long): void 100% (1/1)65%  (15/23)50%  (2/4)
releaseWriteBuffers (SocketSession): void 100% (1/1)70%  (16/23)67%  (6/9)
read (SocketSession): void 100% (1/1)71%  (65/91)74%  (19.2/26)
addSession (SocketSession): void 100% (1/1)72%  (26/36)91%  (7.3/8)
scheduleFlush (SocketSession): void 100% (1/1)72%  (13/18)93%  (3.7/4)
scheduleRemove (SocketSession): void 100% (1/1)72%  (13/18)93%  (3.7/4)
addSessions (): void 100% (1/1)78%  (47/60)83%  (15.7/19)
processSessions (Set): void 100% (1/1)90%  (27/30)91%  (10/11)
SocketIoProcessor (): void 100% (1/1)100% (29/29)100% (8/8)
flushSession (SocketSession): void 100% (1/1)100% (8/8)100% (3/3)
getInstance (): SocketIoProcessor 100% (1/1)100% (2/2)100% (1/1)
notifyIdleSession (SocketSession, long): void 100% (1/1)100% (54/54)100% (6/6)
notifyIdleSessions (): void 100% (1/1)100% (38/38)100% (11/11)
removeSession (SocketSession): void 100% (1/1)100% (10/10)100% (4/4)
startupWorker (): void 100% (1/1)100% (13/13)100% (4/4)
     
class SocketIoProcessor$Worker100% (1/1)100% (2/2)80%  (66/82)71%  (17/24)
run (): void 100% (1/1)79%  (59/75)67%  (14/21)
SocketIoProcessor$Worker (SocketIoProcessor): void 100% (1/1)100% (7/7)100% (3/3)

1/*
2 *   @(#) $Id: SocketIoProcessor.java 327113 2005-10-21 06:59:15Z trustin $
3 *
4 *   Copyright 2004 The Apache Software Foundation
5 *
6 *   Licensed under the Apache License, Version 2.0 (the "License");
7 *   you may not use this file except in compliance with the License.
8 *   You may obtain a copy of the License at
9 *
10 *       http://www.apache.org/licenses/LICENSE-2.0
11 *
12 *   Unless required by applicable law or agreed to in writing, software
13 *   distributed under the License is distributed on an "AS IS" BASIS,
14 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 *   See the License for the specific language governing permissions and
16 *   limitations under the License.
17 *
18 */
19package org.apache.mina.io.socket;
20 
21import java.io.IOException;
22import java.nio.channels.CancelledKeyException;
23import java.nio.channels.SelectionKey;
24import java.nio.channels.Selector;
25import java.nio.channels.SocketChannel;
26import java.util.Iterator;
27import java.util.Set;
28 
29import org.apache.mina.common.ByteBuffer;
30import org.apache.mina.common.IdleStatus;
31import org.apache.mina.common.SessionConfig;
32import org.apache.mina.io.WriteTimeoutException;
33import 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 */
42class 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            // Retry later if session is not yet fully initialized.
195            // (In case that Session.close() is called before addSession() is processed)
196            if( key == null )
197            {
198                scheduleRemove( session );
199                break;
200            }
201 
202            // skip if channel is already closed
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        // process idle sessions
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            // If encountered write request before session is initialized, 
418            // (In case that Session.write() is called before addSession() is processed)
419            if( session.getSelectionKey() == null )
420            {
421                // Reschedule for later write
422                scheduleFlush( session );
423                break;
424            }
425            else
426            {
427                try
428                {
429                    flush( session );
430                }
431                catch( CancelledKeyException e )
432                {
433                    // Connection is closed unexpectedly.
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                    // Kernel buffer is full
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}

[all classes][org.apache.mina.io.socket]
EMMA 2.0.4217 (C) Vladimir Roubtsov