divconq.ctp.CtpAdapter.java Source code

Java tutorial

Introduction

Here is the source code for divconq.ctp.CtpAdapter.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;

import divconq.ctp.cmd.ResponseCommand;
import divconq.hub.Hub;
import divconq.lang.op.FuncCallback;
import divconq.lang.op.OperationContext;
import divconq.log.Logger;
import divconq.struct.RecordStruct;
import divconq.work.IWork;
import divconq.work.Task;
import divconq.work.TaskRun;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFutureListener;

public class CtpAdapter {
    protected boolean switchingProtocols = false;
    protected CtpCommandMapper mapper = CtpCommandMapper.instance; // for basic operations
    protected ICommandHandler handler = null;
    protected ICtpChannel channel = null;
    protected OperationContext context = null;

    protected CtpCommand current = null;

    protected FuncCallback<RecordStruct> currCallback = null;

    public void setChannel(ICtpChannel v) {
        this.channel = v;
    }

    public ICtpChannel getChannel() {
        return this.channel;
    }

    protected void setSwitchingProtocols(boolean v) {
        this.switchingProtocols = v;
    }

    public boolean isSwitchingProtocols() {
        return this.switchingProtocols;
    }

    public void setMapper(CtpCommandMapper v) {
        this.mapper = v;
    }

    public void setHandler(ICommandHandler v) {
        this.handler = v;
    }

    public OperationContext getContext() {
        return this.context;
    }

    public CtpAdapter(OperationContext ctx) {
        this.context = ctx;
    }

    public CtpAdapter() {
        this.context = OperationContext.get();
    }

    // used internally
    // return false if need more
    public boolean decode(ByteBuf buf) throws Exception {
        OperationContext.set(this.context);

        // get the command to continue to decode itself
        if (this.current != null)
            return this.current.decode(buf);

        if (buf.readableBytes() < 1)
            return false;

        int cmdtype = buf.readUnsignedByte();

        this.current = this.mapper.map(cmdtype);

        // TODO handle error/close?
        if (this.current == null)
            return false;

        // get the command to decode itself
        return this.current.decode(buf);
    }

    // used internally
    public void handleCommand() {
        OperationContext.set(this.context);

        CtpCommand cmd = this.current;

        this.current = null;

        //System.out.println("Got Command ------ " + cmd.getClass().getName());

        //if (!this.readRequested)
        //   System.out.println("-------- Got Command when no read was requested ------ " + cmd.getClass().getName());

        //this.readRequested = false;

        if (cmd == CtpCommand.ALIVE) {
            //System.out.println("Ctp Adapter Touched");
            // TODO touch session...
            this.read(); // indicate we want another message
        } else if (cmd == CtpCommand.EXIT_NO_SIGN_OUT) {
            // TODO exit
            this.close();
        } else if (cmd == CtpCommand.EXIT_SIGN_OUT) {
            // TODO sign out and exit...
            this.close();
        } else if (cmd instanceof ResponseCommand) {
            FuncCallback<RecordStruct> cb = this.currCallback;

            if (cb != null) {
                this.currCallback = null;

                // TODO - rather than create a new task all the time, possibly make this ctpfclient class be an always running
                // never timing out task with state that can be resumed intermedentately to do different tasks...
                // then use with Progress and Read, etc too.
                // maybe set timeout to 30 minutes and close connection if idle that long? or just have the keep alive scheduler
                // also touch this?

                // put the call back into the work pool, don't tie up the IO thread 
                Task t = new Task().withContext(cb.getContext().subContext()).withWork(new IWork() {
                    @Override
                    public void run(TaskRun trun) {
                        RecordStruct res = ((ResponseCommand) cmd).getResult();

                        if (res != null) {
                            trun.getContext().logResult(res);
                            cb.setResult(res);
                        }

                        cb.complete();

                        // no, let caller decide when to read
                        //CtpAdapter.this.read();

                        trun.complete();
                    }
                });

                Hub.instance.getWorkPool().submit(t);
            } else {
                // if we get a response but no callback is in queue - TODO log?

                // be sure to read (or close)
                this.read();
            }
        } else {
            // make sure handle runs in WorkPool so IO threads are free
            try {
                this.handler.handle(cmd, this);
            } catch (Exception x) {
                // TODO exit/abort
                System.out.println("Error with command handler: " + x);
            }
        }
    }

    // when using this be sure to issue "adapter.read()" in callback
    public void sendCommand(CtpCommand cmd, FuncCallback<RecordStruct> cb) throws Exception {
        this.currCallback = cb;

        this.sendCommandNotify(cmd, null);
    }

    public void sendCommand(CtpCommand cmd) throws Exception {
        this.sendCommandNotify(cmd, null);
    }

    public void sendCommandNotify(CtpCommand cmd, ChannelFutureListener listener) throws Exception {
        ByteBuf buf = cmd.encode();

        //System.out.println("writing buffer: " + buf.readableBytes());

        // TODO write to channel, not release
        //buf.release();

        try {
            if (this.channel != null)
                this.channel.send(buf, listener);
        } catch (Exception x) {
            Logger.error("Error writing Ctp message: " + cmd);
            Logger.error("Error writing Ctp message: " + x);

            this.close();
        }
    }

    //protected boolean init = false;

    public void read() {
        if (this.switchingProtocols) {
            // TODO send CTP_S_CMD_RESPONSE_SUCCESS when read called, if
        }

        if (this.channel != null)
            this.channel.read();

        /*
        if (!this.init) {
           this.init = true;
           this.current = new InitCommand();
           this.handleCommand();
        }
        */
    }

    public void close() {
        // TODO more

        if (this.channel != null) {
            this.channel.close();
            this.channel = null;
        }

        this.handler.close();
    }
}