NIOAsynchChannel xref

View Javadoc

    1   /***
    2    *
    3    * Copyright 2004 Hiram Chirino
    4    *
    5    * Licensed under the Apache License, Version 2.0 (the "License");
    6    * you may not use this file except in compliance with the License.
    7    * You may obtain a copy of the License at
    8    *
    9    * http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   **/
    18
    19  package org.activeio.net;
    20
    21  import java.io.IOException;
    22  import java.io.InterruptedIOException;
    23  import java.net.SocketException;
    24  import java.nio.ByteBuffer;
    25  import java.nio.channels.SelectionKey;
    26  import java.nio.channels.SocketChannel;
    27
    28  import org.activeio.AsynchChannel;
    29  import org.activeio.AsynchChannelListener;
    30  import org.activeio.Packet;
    31  import org.activeio.Packet.ByteSequence;
    32  import org.activeio.net.NIOAsynchChannelSelectorManager.SelectorManagerListener;
    33  import org.activeio.net.NIOAsynchChannelSelectorManager.SocketChannelAsynchChannelSelection;
    34  import org.activeio.packet.ByteBufferPacket;
    35  import org.activeio.packet.EOSPacket;
    36
    37  /***
    38   * @version $Revision$
    39   */
    40  final public class NIOAsynchChannel extends NIOBaseChannel implements AsynchChannel {
    41
    42      private AsynchChannelListener channelListener;
    43      private SocketChannelAsynchChannelSelection selection;
    44      private ByteBuffer inputByteBuffer;
    45      private boolean running;
    46
    47      public NIOAsynchChannel(SocketChannel socketChannel, boolean useDirect) throws IOException {
    48          super(socketChannel, useDirect);
    49
    50          socketChannel.configureBlocking(false);
    51          selection = NIOAsynchChannelSelectorManager.register(socketChannel, new SelectorManagerListener(){
    52              public void onSelect(SocketChannelAsynchChannelSelection selection) {
    53                  String origName = Thread.currentThread().getName();
    54                  if (selection.isReadable())
    55                  try {
    56                      Thread.currentThread().setName(NIOAsynchChannel.this.toString());
    57                      serviceRead();
    58                   } catch ( Throwable e ) {
    59                       System.err.println("ActiveIO unexpected error: ");
    60                       e.printStackTrace(System.err);
    61                   } finally {
    62                       Thread.currentThread().setName(origName);
    63                   }
    64              }
    65          });
    66
    67      }
    68
    69      private void serviceRead() {
    70          try {
    71
    72              while( true ) {
    73
    74  	            if( inputByteBuffer==null || !inputByteBuffer.hasRemaining() ) {
    75  	                inputByteBuffer = allocateBuffer();
    76  	            }
    77
    78  	            int size = socketChannel.read(inputByteBuffer);
    79  	            if( size == -1 ) {
    80  	                this.channelListener.onPacket( EOSPacket.EOS_PACKET );
    81  	                selection.close();
    82  	                break;
    83  	            }
    84
    85  	            if( size==0 ) {
    86  	                break;
    87  	            }
    88
    89  	            // Per Mike Spile, some plaforms read 1 byte of data on the first read, and then
    90  	            // a but load of data on the second read.  Try to load the butload here
    91  	            if( size == 1 && inputByteBuffer.hasRemaining() ) {
    92  		            int size2 = socketChannel.read(inputByteBuffer);
    93  		            if( size2 > 0 )
    94  		            		size += size2;
    95  	            }
    96
    97  	            ByteBuffer remaining = inputByteBuffer.slice();
    98  	            Packet data = new ByteBufferPacket(((ByteBuffer)inputByteBuffer.flip()).slice());
    99  	            this.channelListener.onPacket( data );
    100
    101 	            // Keep the remaining buffer around to fill with data.
    102 	            inputByteBuffer = remaining;
    103
    104 	            if( inputByteBuffer.hasRemaining() )
    105 	                break;
    106             }
    107
    108         } catch (IOException e) {
    109             this.channelListener.onPacketError(e);
    110         }
    111     }
    112
    113     synchronized public void write(Packet packet) throws IOException {
    114
    115     	ByteBuffer data;
    116         if( packet.getClass()==ByteBufferPacket.class ) {
    117             data = ((ByteBufferPacket)packet).getByteBuffer();
    118         } else {
    119         	ByteSequence sequence = packet.asByteSequence();
    120         	data = ByteBuffer.wrap(sequence.getData(), sequence.getOffset(), sequence.getLength());
    121         }
    122
    123         long delay=1;
    124         while( data.hasRemaining() ) {
    125
    126             // Since the write is non-blocking, all the data may not have been written.
    127             int r1 = data.remaining();
    128 	        socketChannel.write( data );
    129 	        int r2 = data.remaining();
    130
    131 	        // We may need to do a little bit of sleeping to avoid a busy loop.
    132             // Slow down if no data was written out..
    133 	        if( r2>0 && r1-r2==0 ) {
    134 	            try {
    135                     // Use exponential rollback to increase sleep time.
    136                     Thread.sleep(delay);
    137                     delay *= 5;
    138                     if( delay > 1000*1 ) {
    139                         delay = 1000;
    140                     }
    141                 } catch (InterruptedException e) {
    142                     throw new InterruptedIOException();
    143                 }
    144 	        } else {
    145 	            delay = 1;
    146 	        }
    147         }
    148     }
    149
    150     public void flush() throws IOException {
    151     }
    152
    153     public void setAsynchChannelListener(AsynchChannelListener channelListener) {
    154         this.channelListener = channelListener;
    155     }
    156
    157     public AsynchChannelListener getAsynchChannelListener() {
    158         return channelListener;
    159     }
    160
    161     public void dispose() {
    162         if( running && channelListener!=null ) {
    163             channelListener.onPacketError(new SocketException("Socket closed."));
    164         }
    165         selection.close();
    166         super.dispose();
    167     }
    168
    169     public void start() throws IOException {
    170         if( running )
    171             return;
    172         running=true;
    173         selection.setInterestOps(SelectionKey.OP_READ);
    174     }
    175
    176     public void stop(long timeout) throws IOException {
    177         if( !running )
    178             return;
    179         running=false;
    180         selection.setInterestOps(0);
    181     }
    182  }