com.tera.common.network.nio.MMOConnection.java Source code

Java tutorial

Introduction

Here is the source code for com.tera.common.network.nio.MMOConnection.java

Source

/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package com.tera.common.network.nio;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.CancelledKeyException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;

import javolution.util.FastList;

import org.apache.commons.lang.NotImplementedException;

import com.tera.common.idfactory.IdFactory;
import com.tera.common.idfactory.SimpleIdFactory;
import com.tera.common.model.callback.CDeferredCallbackHandler;
import com.tera.common.model.callback.DeferredCallbackHandler;
import com.tera.common.network.model.AbstractNetworkChannel;
import com.tera.common.network.model.ChannelHandler;
import com.tera.common.network.model.ChannelState;
import com.tera.common.network.packet.NetworkPacket;
import com.tera.common.network.packet.PacketService;

/**
 * @author KenM
 * @modified ATracer
 */
public abstract class MMOConnection extends AbstractNetworkChannel implements ChannelHandler {

    private final SelectorThread selectorThread;
    private final ReadWriteThread readWriteThread;
    private final Socket socket;
    private InetAddress inetAddress;
    private String hostAddress;

    private FastList<NetworkPacket> sendQueue;
    private final SelectionKey selectionKey;

    private ByteBuffer readBuffer;
    private ByteBuffer primaryWriteBuffer;
    private ByteBuffer secondaryWriteBuffer;

    private long closeTimeout = -1;

    private DeferredCallbackHandler<MMOConnection> closeListeners = new CDeferredCallbackHandler<MMOConnection>();

    private static IdFactory idFactory = new SimpleIdFactory();
    private final int connectionId;

    protected MMOConnection(PacketService packetService, SelectorThread selectorThread, SocketChannel socketChannel)
            throws ClosedChannelException {
        super(packetService);
        this.selectorThread = selectorThread;
        this.readWriteThread = getSelectorThread().getReadWriteThread();
        this.socket = socketChannel.socket();
        this.inetAddress = socket.getInetAddress();
        this.hostAddress = inetAddress.getHostAddress();
        this.selectionKey = socketChannel.register(getReadWriteThread().getSelector(), SelectionKey.OP_READ);
        this.selectionKey.attach(this);
        setChannelState(ChannelState.CONNECTED);
        connectionId = idFactory.nextId();//TODO deallocate after disconnect
    }

    @Override
    public Object getChannel() {
        return getSocketChannel();
    }

    @Override
    public ChannelHandler getHandler() {
        throw new NotImplementedException();
    }

    @Override
    public final boolean isConnected() {
        return !isClosed();// temporary
    }

    @Override
    public final int getChannelId() {
        return connectionId;
    }

    final boolean closeTimeouted() {
        return System.currentTimeMillis() > closeTimeout;
    }

    @Override
    public synchronized void write(NetworkPacket networkPacket) {
        if (isClosed())
            return;

        try {
            getSelectionKey().interestOps(getSelectionKey().interestOps() | SelectionKey.OP_WRITE);
            getSendQueue2().addLast(networkPacket);
        } catch (CancelledKeyException e) {
            // ignore
        }
    }

    @Override
    public synchronized void writeAndClose(NetworkPacket networkPacket) {
        if (isClosed())
            return;

        getSendQueue2().clear();
        write(networkPacket);

        // let the client have the chance to get all of the packets
        // even for a connection with issues 10'000 msec should be enough to write
        // the pending packets
        closeTimeout = System.currentTimeMillis() + 10000;

        disableReadInterest();
        getReadWriteThread().closeConnection(this);
    }

    @Override
    public synchronized void close() {
        if (isClosed())
            return;

        getSendQueue2().clear();

        // close the client as soon as possible
        // for a normal connection 100 msec should be enough to write the pending
        // packets
        closeTimeout = System.currentTimeMillis() + 100;

        disableReadInterest();
        getReadWriteThread().closeConnection(this);
    }

    /**
     * Notify all close listeners that connection was closed. This event will be
     * received after all necessary connection resources was cleaned by worker
     * thread.
     * 
     * @param forced
     */
    public void notifyConnectionClosed(boolean forced) {
        closeListeners.operationComplete(this);
    }

    @Override
    public InetSocketAddress getAddress() {
        return (InetSocketAddress) socket.getRemoteSocketAddress();
    }

    @Override
    public String getIpAddress() {
        return hostAddress;
    }

    final SelectorThread getSelectorThread() {
        return selectorThread;
    }

    private ReadWriteThread getReadWriteThread() {
        return readWriteThread;
    }

    final SelectionKey getSelectionKey() {
        return selectionKey;
    }

    final void disableReadInterest() {
        try {
            getSelectionKey().interestOps(getSelectionKey().interestOps() & ~SelectionKey.OP_READ);
        } catch (CancelledKeyException e) {
            // ignore
        }
    }

    final void disableWriteInterest() {
        try {
            getSelectionKey().interestOps(getSelectionKey().interestOps() & ~SelectionKey.OP_WRITE);
        } catch (CancelledKeyException e) {
            // ignore
        }
    }

    final Socket getSocket() {
        return socket;
    }

    final SocketChannel getSocketChannel() {
        return socket.getChannel();
    }

    final FastList<NetworkPacket> getSendQueue2() {
        if (sendQueue == null)
            sendQueue = new FastList<NetworkPacket>();

        return sendQueue;
    }

    final void createWriteBuffer(ByteBuffer buf) {
        if (primaryWriteBuffer == null) {
            // APPENDING FOR NULL

            primaryWriteBuffer = getReadWriteThread().getPooledBuffer();
            primaryWriteBuffer.put(buf);
        } else {
            // PREPENDING ON EXISTING

            ByteBuffer temp = getReadWriteThread().getPooledBuffer();
            temp.put(buf);

            int remaining = temp.remaining();
            primaryWriteBuffer.flip();
            int limit = primaryWriteBuffer.limit();

            if (remaining >= primaryWriteBuffer.remaining()) {
                temp.put(primaryWriteBuffer);
                getReadWriteThread().recycleBuffer(primaryWriteBuffer);
                primaryWriteBuffer = temp;
            } else {
                primaryWriteBuffer.limit(remaining);
                temp.put(primaryWriteBuffer);
                primaryWriteBuffer.limit(limit);
                primaryWriteBuffer.compact();
                secondaryWriteBuffer = primaryWriteBuffer;
                primaryWriteBuffer = temp;
            }
        }
    }

    final boolean hasPendingWriteBuffer() {
        return primaryWriteBuffer != null;
    }

    final void movePendingWriteBufferTo(ByteBuffer dest) {
        primaryWriteBuffer.flip();
        dest.put(primaryWriteBuffer);
        getReadWriteThread().recycleBuffer(primaryWriteBuffer);
        primaryWriteBuffer = secondaryWriteBuffer;
        secondaryWriteBuffer = null;
    }

    final void setReadBuffer(ByteBuffer buf) {
        readBuffer = buf;
    }

    final ByteBuffer getReadBuffer() {
        return readBuffer;
    }

    final boolean isClosed() {
        return closeTimeout != -1;
    }

    final void releaseBuffers() {
        if (primaryWriteBuffer != null) {
            getReadWriteThread().recycleBuffer(primaryWriteBuffer);
            primaryWriteBuffer = null;
            if (secondaryWriteBuffer != null) {
                getReadWriteThread().recycleBuffer(secondaryWriteBuffer);
                secondaryWriteBuffer = null;
            }
        }

        if (readBuffer != null) {
            getReadWriteThread().recycleBuffer(readBuffer);
            readBuffer = null;
        }
    }

    protected abstract boolean decrypt(ByteBuffer buf, int size);

    protected abstract void encrypt(ByteBuffer buf, int size);
}