divconq.ctp.net.CtpHandler.java Source code

Java tutorial

Introduction

Here is the source code for divconq.ctp.net.CtpHandler.java

Source

/* ************************************************************************
#
#  DivConq
#
#  http://divconq.com/
#
#  Copyright:
#    Copyright 2014 eTimeline, LLC. All rights reserved.
#
#  License:
#    See the license.txt file in the project's top-level directory for details.
#
#  Authors:
#    * Andy White
#
************************************************************************ */
package divconq.ctp.net;

import java.util.concurrent.locks.ReentrantLock;

import divconq.ctp.CtpAdapter;
import divconq.ctp.ICtpChannel;
import divconq.hub.Hub;
import divconq.log.Logger;
import divconq.net.ssl.SslHandshakeCompletionEvent;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class CtpHandler extends ChannelInboundHandlerAdapter implements ICtpChannel {
    protected CtpAdapter adapter = null;
    protected ChannelHandlerContext chan = null;
    protected boolean serverMode = true;

    protected ByteBuf remnant = null;
    protected boolean readRequested = true; // initially expects a read
    protected ReentrantLock readLock = new ReentrantLock();

    public CtpHandler(CtpAdapter tunnel, boolean serverMode) {
        this.adapter = tunnel;
        this.serverMode = serverMode;
    }

    public void debug(String msg) {
        System.out.println("Ctp " + (this.serverMode ? "Server" : "Client") + " - " + msg);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        this.adapter.close();
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        this.debug("Handler added");
        this.chan = ctx;
        this.adapter.setChannel(this);
    }

    @Override
    public void read() {
        this.readLock.lock();

        if (serverMode)
            System.out.println("Start Server requested read!");

        this.readRequested = false; // meaning read is covered until further notice

        try {
            ByteBuf rem = this.remnant;

            //CtpHandler.this.debug("checking remnant: " + rem);

            // if there are any unread bytes from the last read, check to see if we can collect a command
            if (rem != null) {
                //CtpHandler.this.debug("checking bytes: " + rem.readableBytes());

                //System.out.println("Remnant ref cnt 1: " + rem.refCnt() + " for server: " + CtpHandler.this.serverMode);

                boolean ready = false;

                try {
                    ready = this.adapter.decode(rem);
                } catch (Exception x) {
                    // TODO error and close!!
                    System.out.println("Error decoding message: " + x);
                    return;
                }

                //System.out.println("Remnant ref cnt 2: " + rem.refCnt());

                // if there are any unread bytes here we need to store them and combine with the next read
                if (!rem.isReadable()) {
                    this.remnant = null;
                    rem.release();
                }

                //System.out.println("Remnant ref cnt 3: " + rem.refCnt());

                if (!ready) {
                    this.readRequested = true;
                    this.chan.read();
                } else
                    this.adapter.handleCommand();
            } else {
                this.readRequested = true;
                this.chan.read();
            }

            if (serverMode)
                System.out.println("End Server requested read!");
        } finally {
            this.readLock.unlock();
        }
    }

    @Override
    public void send(ByteBuf buf, ChannelFutureListener listener) {
        try {
            if (this.chan != null) {
                ChannelFuture future = this.chan.writeAndFlush(buf);

                if (listener != null)
                    future.addListener(listener);
            }
        } catch (Exception x) {
            Logger.error("Error writing Ctp buffer: " + x);

            this.close();
        }
    }

    @Override
    public void close() {
        try {
            if (this.chan != null)
                this.chan.close().await(2000);
        } catch (InterruptedException x) {
            // ignore 
        } finally {
            this.chan = null;
        }
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        CtpHandler.this.readLock.lock();

        try {
            ByteBuf buf = (ByteBuf) msg;

            if (serverMode)
                System.out.println("Server got network read 1: " + buf.readableBytes());

            ByteBuf rem = this.remnant;

            // if there are any unread bytes from the last read, combine with this read 
            this.remnant = buf;

            // TODO there are maybe better ways to do this - a queue of buffers?
            if (rem != null) {
                if (rem.isReadable()) {
                    this.remnant = Hub.instance.getBufferAllocator()
                            .heapBuffer(rem.readableBytes() + buf.readableBytes());
                    this.remnant.writeBytes(rem);
                    this.remnant.writeBytes(buf);

                    buf.release();
                }

                rem.release();
            }

            if (serverMode)
                System.out.println("Server got network read 2: " + this.remnant.readableBytes());

            if (!this.readRequested)
                return;

            if (this.remnant.readableBytes() > 256 * 1024)
                System.out.println(
                        "CTP Buffer getting too large - possible issue!!!! " + this.remnant.readableBytes());

            // read with the updated buffer
            this.read();
        } finally {
            CtpHandler.this.readLock.unlock();
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //ctx.flush();

        //System.out.println("read complete");
        //this.debug("Read Complete");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        this.debug("Exception");

        cause.printStackTrace();
        ctx.close();
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {

        // on success the request first read
        if (evt == SslHandshakeCompletionEvent.SUCCESS) {
            // make sure auto read is off
            ctx.channel().config().setAutoRead(false);

            this.debug("SSL passed");

            // do initial read, this is fine - just be sure streaming will work by not always reading
            ctx.read();
        }
    }
}