Package com.google.code.yanf4j.nio.impl

Source Code of com.google.code.yanf4j.nio.impl.AbstractNioSession

package com.google.code.yanf4j.nio.impl;

import java.io.IOException;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.channels.CancelledKeyException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;

import com.google.code.yanf4j.buffer.IoBuffer;
import com.google.code.yanf4j.core.EventType;
import com.google.code.yanf4j.core.WriteMessage;
import com.google.code.yanf4j.core.impl.AbstractSession;
import com.google.code.yanf4j.nio.NioSession;
import com.google.code.yanf4j.nio.NioSessionConfig;
import com.google.code.yanf4j.util.SelectorFactory;

/**
* Abstract nio session
*
* @author dennis
*
*/
public abstract class AbstractNioSession extends AbstractSession implements
    NioSession {

  public SelectableChannel channel() {
    return selectableChannel;
  }

  protected SelectorManager selectorManager;
  protected SelectableChannel selectableChannel;

  public AbstractNioSession(NioSessionConfig sessionConfig) {
    super(sessionConfig);
    selectorManager = sessionConfig.selectorManager;
    selectableChannel = sessionConfig.selectableChannel;
  }

  public final void enableRead(Selector selector) {
    SelectionKey key = selectableChannel.keyFor(selector);
    if (key != null && key.isValid()) {
      interestRead(key);
    } else {
      try {
        selectableChannel
            .register(selector, SelectionKey.OP_READ, this);
      } catch (ClosedChannelException e) {
        // ignore
      } catch (CancelledKeyException e) {
        // ignore
      }
    }
  }

  private void interestRead(SelectionKey key) {
    if (key.attachment() == null) {
      key.attach(this);
    }
    key.interestOps(key.interestOps() | SelectionKey.OP_READ);
  }

  @Override
  protected void start0() {
    registerSession();
  }

  public InetAddress getLocalAddress() {
    return ((SocketChannel) selectableChannel).socket().getLocalAddress();
  }

  protected abstract Object writeToChannel(WriteMessage msg)
      throws ClosedChannelException, IOException;

  protected void onWrite(SelectionKey key) {
    boolean isLockedByMe = false;
    if (currentMessage.get() == null) {
      // get next message
      WriteMessage nextMessage = writeQueue.peek();
      if (nextMessage != null && writeLock.tryLock()) {
        if (!writeQueue.isEmpty()
            && currentMessage.compareAndSet(null, nextMessage)) {
          writeQueue.remove();
        }
      } else {
        return;
      }
    } else if (!writeLock.tryLock()) {
      return;
    }
    updateTimeStamp();

    isLockedByMe = true;
    WriteMessage currentMessage = null;
    // make read/write fail, write/read=3/2
    final long maxWritten = readBuffer.capacity() + readBuffer.capacity() >>> 1;
    try {
      long written = 0;
      while (this.currentMessage.get() != null) {
        currentMessage = this.currentMessage.get();
        currentMessage = preprocessWriteMessage(currentMessage);
        this.currentMessage.set(currentMessage);
        long before = this.currentMessage.get().getWriteBuffer()
            .remaining();
        Object writeResult = null;

        if (written < maxWritten) {
          writeResult = writeToChannel(currentMessage);
          written += this.currentMessage.get().getWriteBuffer()
              .remaining()
              - before;
        } else {
          // wait for next time to write
        }
        // write complete
        if (writeResult != null) {
          this.currentMessage.set(writeQueue.poll());
          handler.onMessageSent(this, currentMessage.getMessage());
          // try to get next message
          if (this.currentMessage.get() == null) {
            if (isLockedByMe) {
              isLockedByMe = false;
              writeLock.unlock();
            }
            // get next message
            WriteMessage nextMessage = writeQueue.peek();
            if (nextMessage != null && writeLock.tryLock()) {
              isLockedByMe = true;
              if (!writeQueue.isEmpty()
                  && this.currentMessage.compareAndSet(null,
                      nextMessage)) {
                writeQueue.remove();
              }
              continue;
            } else {
              break;
            }
          }
        } else {
          // does't write complete
          if (isLockedByMe) {
            isLockedByMe = false;
            writeLock.unlock();
          }
          // register OP_WRITE event
          selectorManager.registerSession(this,
              EventType.ENABLE_WRITE);
          break;
        }
      }
    } catch (IOException e) {
      handler.onExceptionCaught(this, e);
      if (currentMessage != null
          && currentMessage.getWriteFuture() != null) {
        currentMessage.getWriteFuture().failure(e);
      }
      if (isLockedByMe) {
        isLockedByMe = false;
        writeLock.unlock();
      }
      close();
    } finally {
      if (isLockedByMe) {
        writeLock.unlock();
      }
    }
  }

  public final void enableWrite(Selector selector) {
    SelectionKey key = selectableChannel.keyFor(selector);
    if (key != null && key.isValid()) {
      interestWrite(key);
    } else {
      try {
        selectableChannel.register(selector,
            SelectionKey.OP_WRITE, this);
      } catch (ClosedChannelException e) {
        // ignore
      } catch (CancelledKeyException e) {
        // ignore
      }
    }
  }

  private void interestWrite(SelectionKey key) {
    if (key.attachment() == null) {
      key.attach(this);
    }
    key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
  }

  protected void onRead(SelectionKey key) {
    updateTimeStamp();
    readFromBuffer();
  }

  protected abstract void readFromBuffer();

  @Override
  protected void closeChannel() throws IOException {
    flush0();
    unregisterSession();
    unregisterChannel();
  }

  protected final void unregisterChannel() throws IOException {
    writeLock.lock();
    try {
      if (getAttribute(SelectorManager.REACTOR_ATTRIBUTE) != null) {
        ((Reactor) getAttribute(SelectorManager.REACTOR_ATTRIBUTE))
            .unregisterChannel(selectableChannel);
      }
      if (selectableChannel.isOpen()) {
        selectableChannel.close();
      }
    } finally {
      writeLock.unlock();
    }
  }

  protected final void registerSession() {
    selectorManager.registerSession(this, EventType.REGISTER);
  }

  protected void unregisterSession() {
    selectorManager.registerSession(this, EventType.UNREGISTER);
  }

  @Override
  public void writeFromUserCode(WriteMessage message) {
    if (schduleWriteMessage(message)) {
      return;
    }
    // 到这里,当前线程一定是IO线程
    onWrite(null);

  }

  protected boolean schduleWriteMessage(WriteMessage writeMessage) {
    boolean offered = writeQueue.offer(writeMessage);
    assert offered;
    final Reactor reactor = selectorManager.getReactorFromSession(this);
    if (Thread.currentThread() != reactor) {
      selectorManager.registerSession(this, EventType.ENABLE_WRITE);
      return true;
    }
    return false;
  }

  public void flush() {
    if (isClosed()) {
      return;
    }
    flush0();
  }

  protected final void flush0() {
    SelectionKey tmpKey = null;
    Selector writeSelector = null;
    int attempts = 0;
    try {
      while (true) {
        if (writeSelector == null) {
          writeSelector = SelectorFactory.getSelector();
          if (writeSelector == null) {
            return;
          }
          tmpKey = selectableChannel.register(writeSelector,
              SelectionKey.OP_WRITE);
        }
        if (writeSelector.select(1000) == 0) {
          attempts++;
          if (attempts > 2) {
            return;
          }
        } else {
          break;
        }
      }
      onWrite(selectableChannel.keyFor(writeSelector));
    } catch (ClosedChannelException cce) {
      onException(cce);
      log.error("Flush error", cce);
      close();
    } catch (IOException ioe) {
      onException(ioe);
      log.error("Flush error", ioe);
      close();
    } finally {
      if (tmpKey != null) {
        // Cancel the key.
        tmpKey.cancel();
        tmpKey = null;
      }
      if (writeSelector != null) {
        try {
          writeSelector.selectNow();
        } catch (IOException e) {
          log.error("Temp selector selectNow error", e);
        }
        // return selector
        SelectorFactory.returnSelector(writeSelector);
      }
    }
  }

  protected final long doRealWrite(SelectableChannel channel, IoBuffer buffer)
      throws IOException {
    if (log.isDebugEnabled()) {
      StringBuffer bufMsg = new StringBuffer("send buffers:\n[\n");
      final ByteBuffer buff = buffer.buf();
      bufMsg.append(" buffer:position=").append(buff.position()).append(
          ",limit=").append(buff.limit()).append(",capacity=")
          .append(buff.capacity()).append("\n");

      bufMsg.append("]");
      log.debug(bufMsg.toString());
    }
    return ((WritableByteChannel) channel).write(buffer.buf());
  }

  /**
   * �ɷ�IO�¼�
   */
  public final void onEvent(EventType event, Selector selector) {
    if (isClosed()) {
      return;
    }
    SelectionKey key = selectableChannel.keyFor(selector);

    switch (event) {
    case EXPIRED:
      onExpired();
      break;
    case WRITEABLE:
      onWrite(key);
      break;
    case READABLE:
      onRead(key);
      break;
    case ENABLE_WRITE:
      enableWrite(selector);
      break;
    case ENABLE_READ:
      enableRead(selector);
      break;
    case IDLE:
      onIdle();
      break;
    case CONNECTED:
      onConnected();
      break;
    default:
      log.error("Unknown event:" + event.name());
      break;
    }
  }
}
TOP

Related Classes of com.google.code.yanf4j.nio.impl.AbstractNioSession

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.