org.cloudata.core.commitlog.pipe.RunState.java Source code

Java tutorial

Introduction

Here is the source code for org.cloudata.core.commitlog.pipe.RunState.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.cloudata.core.commitlog.pipe;

import java.io.IOException;
import java.lang.reflect.UndeclaredThrowableException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.cloudata.core.commitlog.CommitLogServer;
import org.cloudata.core.commitlog.CommitLogServerIF;
import org.cloudata.core.commitlog.CommitLogShutDownException;
import org.cloudata.core.commitlog.pipe.Bulk.OperationResult;
import org.cloudata.core.commitlog.pipe.Pipe.Context;
import org.cloudata.core.common.testhelper.FaultInjectionProxy;
import org.cloudata.core.common.testhelper.ProxyExceptionHelper;

class RunState extends PipeState implements RunStateTestIF {
    static final Log LOG = LogFactory.getLog(RunState.class);
    private static final PipeState singleton = new RunState();

    public static PipeState instance() {
        return singleton;
    }

    RunStateTestIF testProxy;

    RunState() {
        testProxy = this;
        if (CommitLogServerIF.IS_TEST_MODE) {
            testProxy = FaultInjectionProxy.wrap(this, RunStateTestIF.class);
        }
    }

    public void init(Pipe.Context ctx) throws IOException {
        ctx.registerToSelect(ctx.prevChannel, SelectionKey.OP_READ);
        ctx.prevChannel.socket().setTcpNoDelay(true);
        ctx.prevChannel.configureBlocking(false);

        if (ctx.nextChannel != null) {
            ctx.registerToSelect(ctx.nextChannel, SelectionKey.OP_READ);
        }

        ctx.bulk = new Bulk();
    }

    public void close(Context ctx) throws IOException {
        ctx.deregisterFromSelectAll(ctx.prevChannel);
        if (ctx.nextChannel != null) {
            ctx.deregisterFromSelectAll(ctx.nextChannel);
        }
        ctx.bulk = null;
    }

    public boolean readFromNext(Pipe.Context ctx) throws IOException {
        int numRead = ctx.nextChannel.read(ctx.ack);

        if (numRead < 0) {
            throw new IOException("Disconnect from next...");
        }

        if (ctx.ack.hasRemaining()) {
            LOG.warn("reading ack is pending in next select time, numRead :" + numRead);
            return false;
        } else {
            //      ctx.ack.flip();
            //
            //      byte ret = ctx.ack.get();
            //      long ackTime = ctx.ack.getLong();
            //      long time = System.currentTimeMillis();
            //      if ((time - ackTime) > 1000) {
            //        LOG.debug("TIME REPORT [too long time to pass ack] takes " + (time - ackTime) + " ms");
            //     }

            ctx.ack.flip();

            LOG.debug("pass ack to previous ctx.pipe : " + ctx.getPipeKey());
            ctx.status.ackReceived = true;

            return writeToPrev(ctx);
        }
    }

    public boolean readFromPrev(Pipe.Context ctx) throws IOException {
        if (ctx.status.dataReceived) {
            ByteBuffer buf = ByteBuffer.allocate(100);
            int numRead = ctx.prevChannel.read(buf);

            if (numRead < 0) {
                throw new IOException("End of Stream");
            }

            LOG.warn("Duplicated reception of data from previous! numRead : " + numRead);
            dump(buf);
            throw new IOException("Duplicated reception of data from previous!");
        }

        OperationResult ret = ctx.bulk.read(ctx.prevChannel);

        if (ctx.status.inReceivingData == false) {
            ctx.status.inReceivingData = true;
            ctx.status.dataReceived = false;
            ctx.status.writingDone = false;
            ctx.status.ackReceived = false;
            ctx.status.ackSent = false;

            // pipe? validity ? timeout? ?? ? 
            // ?.
            ctx.checkValidity = true;
            ctx.startReceivingTime = System.currentTimeMillis();
        }

        if (ret == OperationResult.completed) {
            ctx.endReceivingTime = System.currentTimeMillis();

            if (ctx.endReceivingTime - ctx.startReceivingTime > 100) {
                LOG.info("TIME REPORT [too long time to transfer] takes "
                        + (ctx.endReceivingTime - ctx.startReceivingTime) + "ms");
            }

            ctx.status.inReceivingData = false;
            ctx.status.dataReceived = true;
        }

        writeToNext(ctx);

        return true;
    }

    public boolean writeToNext(Pipe.Context ctx) throws IOException {
        if (ctx.isLastPipe && ctx.status.dataReceived) {
            if (ctx.status.scheduled == false) {
                try {
                    testProxy.writeToLogFile(ctx);
                } catch (UndeclaredThrowableException e) {
                    LOG.error("error in scheduling write", e);
                    ProxyExceptionHelper.handleException(e, LOG);
                }
            } else {
                LOG.warn("tried to schedule again.");
            }

            return true;
        }

        if (ctx.nextChannel != null && ctx.bulk.isWrittingDone() == false) {
            if (ctx.bulk.write(ctx.nextChannel) == OperationResult.partially) {
                if (ctx.selectingWriteNext == false) {
                    ctx.registerToSelect(ctx.nextChannel, SelectionKey.OP_WRITE);
                    ctx.selectingWriteNext = true;
                }

                return false;
            } else if (ctx.status.inReceivingData == false && ctx.bulk.isWrittingDone()) {
                if (ctx.status.scheduled == false) {
                    try {
                        testProxy.writeToLogFile(ctx);
                    } catch (UndeclaredThrowableException e) {
                        LOG.error("error in scheduling write", e);
                        ProxyExceptionHelper.handleException(e, LOG);
                    }
                } else {
                    LOG.warn("tried to schedule again..");
                }

                if (ctx.selectingWriteNext) {
                    ctx.deregisterFromSelect(ctx.nextChannel, SelectionKey.OP_WRITE);
                    ctx.selectingWriteNext = false;
                }
            }
        }

        return true;
    }

    public void writeToLogFile(Pipe.Context ctx) throws IOException {
        try {
            if (ctx.status.scheduled) {
                LOG.warn("WARN!!! ctx is already scheduled pipe key : " + ctx.getPipeKey());
            }

            ctx.scheduleWriting();
            ctx.status.scheduled = true;
        } catch (IOException e) {
            LOG.warn("scheduling writing is fail", e);
            throw e;
        }
    }

    @Override
    public boolean writeFileDone(Context ctx) throws IOException {
        ctx.status.scheduled = false;
        ctx.status.writingDone = true;
        ByteBuffer ret = ByteBuffer.allocate(9);

        if (ctx.signalReceiver.read(ret) < 0) {
            throw new IOException("Internal pipe disconnection");
        }

        ret.flip();
        ctx.bulk.clear();

        byte result = ret.get();
        long time = ret.getLong();

        ctx.writeDoneTime = System.currentTimeMillis();

        CommitLogServer.metrics.setWrite(ctx.bulk.totalNumWritten, (ctx.writeDoneTime - time));

        if (ctx.writeDoneTime - time > 100) {
            LOG.info("TIME REPORT [too long time to be signaled] takes " + (ctx.writeDoneTime - time) + "ms");
        }

        if (result == AsyncFileWriter.NACK) {
            writeFileFail(ctx);
        }

        if (ctx.isLastPipe) {
            ctx.ack.clear();
            ctx.ack.put(AsyncFileWriter.ACK);
            ctx.ack.putLong(System.currentTimeMillis());
            ctx.ack.flip();
            writeToPrev(ctx);
        } else if (ctx.status.ackReceived) {
            writeToPrev(ctx);
        }

        return true;
    }

    private void writeFileFail(Context ctx) throws CommitLogShutDownException {
        String msg = "FAIL TO WRITE COMMIT LOG, THIS SERVER SHOULD BE SHUTDOWN";
        if (ctx.writingError != null) {
            LOG.fatal(msg, ctx.writingError);
        } else {
            LOG.fatal(msg);
        }

        throw new CommitLogShutDownException(
                ctx.writingError != null ? ctx.writingError : new IOException("Writing fail"));
    }

    public boolean writeToPrev(Pipe.Context ctx) throws IOException {
        if (ctx.status.writingDone == false) {
            return false;
        }

        boolean ret = writeAckToPrev(ctx);

        if (ret) {
            ctx.checkValidity = false;

            ctx.status.inReceivingData = false;
            ctx.status.dataReceived = false;
            ctx.status.scheduled = false;
            ctx.status.writingDone = false;
            ctx.status.ackReceived = false;
            ctx.status.ackSent = true;

            ctx.ack.clear();
        } else {
            LOG.warn("reset in next time");
        }

        return ret;
    }

    public String getStateName() {
        return "Run State";
    }

    static void dump(ByteBuffer buf) {
        buf.flip();
        byte[] array = buf.array();
        for (int i = 0; i < array.length; i++) {
            System.out.format("0x%x ", array[i]);
            if ((i + 1) % 8 == 0) {
                System.out.println();
            }
        }
    }
}