com.miraclelinux.historygluon.BridgeWorker.java Source code

Java tutorial

Introduction

Here is the source code for com.miraclelinux.historygluon.BridgeWorker.java

Source

/* History Gluon
   Copyright (C) 2012 MIRACLE LINUX CORPORATION
     
   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.miraclelinux.historygluon;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Iterator;
import java.util.concurrent.BlockingQueue;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class BridgeWorker extends Thread {

    /* -----------------------------------------------------------------------
     * Private constant
     * -------------------------------------------------------------------- */
    private static final byte[] MAGIC_CODE = { 0x48, 0x47, 0x4c, 0x00 };
    private static final int PKT_MAGIC_CODE_LENGTH = 4;
    private static final int PKT_DB_NAME_SIZE_LENGTH = 2;
    private static final int PKT_SIZE_LENGTH = 4;
    private static final int PKT_CMD_LENGTH = 2;
    private static final int PKT_ID_LENGTH = 8;
    private static final int PKT_SEC_LENGTH = 4;
    private static final int PKT_NS_LENGTH = 4;
    private static final int PKT_DATA_TYPE_LENGTH = 2;
    private static final int PKT_DATA_FLOAT_LENGTH = 8;
    private static final int PKT_DATA_UINT64_LENGTH = 8;
    private static final int PKT_DATA_STRING_SIZE_LENGTH = 4;
    private static final int PKT_DATA_BLOB_SIZE_LENGTH = 8;
    private static final int PKT_NUM_ENTRIES_LENGTH = 8;
    private static final int PKT_SORT_ORDER_LENGTH = 2;
    private static final int PKT_QUERY_TYPE_LENGTH = 2;
    private static final int PKT_DELETE_WAY_LENGTH = 2;
    private static final int PKT_NUM_DELETED_LENGTH = 8;

    private static final short PKT_CMD_ADD_DATA = 100;
    private static final short PKT_CMD_QUERY_DATA = 200;
    private static final short PKT_CMD_RANGE_QUERY = 300;
    private static final short PKT_CMD_QUERY_ALL = 310;
    private static final short PKT_CMD_GET_MIN_TIME = 400;
    private static final short PKT_CMD_GET_STATISTICS = 500;
    private static final short PKT_CMD_DELETE = 600;
    private static final short PKT_CMD_DELETE_ALL = 610;

    private static final short PKT_SORT_ORDER_ASCENDING = 0;
    private static final short PKT_SORT_ORDER_DESCENDING = 1;
    private static final short PKT_SORT_ORDER_NOT_SORTED = 2;

    private static final int REPLY_RESULT_LENGTH = 4;

    private static final int RESULT_ERROR_UNKNOWN_REASON = 1;
    private static final int RESULT_ERROR_TOO_MANY_RECORDS = 2;
    private static final int RESULT_ERROR_NO_DATA = 3;

    private static final int STREAM_EVENT_TYPE_END = 0xf000;

    /* -----------------------------------------------------------------------
     * Public constant
     * -------------------------------------------------------------------- */
    public static final long MAX_ENTRIES_UNLIMITED = 0;

    /* -----------------------------------------------------------------------
     * Private members
     * -------------------------------------------------------------------- */
    private Socket m_socket = null;
    private Log m_log = null;
    private BufferedOutputStream m_ostream = null;
    private BufferedInputStream m_istream = null;
    private StorageDriver m_driver = null;
    private ByteBuffer m_byteBuffer = null;
    private ExecTimeObserver m_cmdProcTimeObserver = null;

    /* -----------------------------------------------------------------------
     * Public Methods
     * -------------------------------------------------------------------- */
    public BridgeWorker(Socket socket, StorageDriver driver) {
        m_socket = socket;
        m_log = LogFactory.getLog(BridgeWorker.class);
        m_log.info("start BridgeWorker: host: " + m_socket.getInetAddress().getHostAddress() + ", remote port: "
                + m_socket.getPort());
        m_driver = driver;
        m_byteBuffer = ByteBuffer.allocate(100);
        m_byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
    }

    @Override
    public void run() {
        // make a time observer. This object should be created here,
        // because it uses thread information in it.
        m_cmdProcTimeObserver = new ExecTimeObserver("cmd");
        String env_measure_cmd_proc_time = System.getenv("MEASURE_CMD_PROC_TIME");
        if (env_measure_cmd_proc_time != null && env_measure_cmd_proc_time.equals("1"))
            m_cmdProcTimeObserver.setEnable();

        try {
            m_ostream = new BufferedOutputStream(m_socket.getOutputStream());
            m_istream = new BufferedInputStream(m_socket.getInputStream());
            if (connect()) {
                while (doHandshake())
                    ;
            }
        } catch (Exception e) {
            e.printStackTrace();
            m_log.error(e);
        } finally {
            m_driver.close();
            try {
                m_socket.close();
            } catch (IOException e) {
                m_log.error("failed to close socket: " + e);
            }
        }
    }

    /* -----------------------------------------------------------------------
     * Private Methods
     * -------------------------------------------------------------------- */
    private boolean connect() throws IOException {
        // magic code
        byte[] magicCode = new byte[PKT_MAGIC_CODE_LENGTH];
        if (m_istream.read(magicCode, 0, PKT_MAGIC_CODE_LENGTH) == -1)
            return false;

        boolean magicCodeOk = true;
        for (int i = 0; i < PKT_MAGIC_CODE_LENGTH; i++) {
            if (MAGIC_CODE[i] != magicCode[i]) {
                magicCodeOk = false;
                break;
            }
        }
        if (magicCodeOk == false) {
            String msg;
            msg = String.format("Unexpected magic code: %02x %02x %02x %02x", magicCode[0], magicCode[1],
                    magicCode[2], magicCode[3]);
            m_log.error(msg);
            return false;
        }

        // length of DB name
        byte[] bufLenDBName = new byte[PKT_DB_NAME_SIZE_LENGTH];
        if (m_istream.read(bufLenDBName, 0, PKT_DB_NAME_SIZE_LENGTH) == -1)
            return false;
        m_byteBuffer.clear();
        m_byteBuffer.put(bufLenDBName);
        short dbNameSize = m_byteBuffer.getShort(0);

        // Database name
        byte[] bufDBName = new byte[dbNameSize];
        if (m_istream.read(bufDBName, 0, dbNameSize) == -1)
            return false;
        String dbName = new String(bufDBName);

        // reply
        int length = PKT_MAGIC_CODE_LENGTH + REPLY_RESULT_LENGTH;
        m_byteBuffer.clear();
        m_byteBuffer.put(MAGIC_CODE);
        m_byteBuffer.putInt(ErrorCode.SUCCESS);
        m_ostream.write(m_byteBuffer.array(), 0, m_byteBuffer.position());
        m_ostream.flush();

        // Set the DB name to the storage
        m_driver.setDatabase(dbName);

        return true;
    }

    private boolean doHandshake() throws IOException {

        // packet size
        byte[] sizeBuf = new byte[PKT_SIZE_LENGTH];
        if (m_istream.read(sizeBuf, 0, PKT_SIZE_LENGTH) == -1) {
            return false;
        }
        m_byteBuffer.clear();
        m_byteBuffer.put(sizeBuf);
        int pktSize = m_byteBuffer.getInt(0);

        // read pkt body
        byte[] pktBuf = new byte[pktSize];
        if (m_istream.read(pktBuf, 0, pktSize) == -1) {
            m_log.info("Unexpectedly input stream reaches the end.");
            return false;
        }

        // start measurement
        m_cmdProcTimeObserver.start();

        // get command
        m_byteBuffer.clear();
        m_byteBuffer.put(pktBuf, 0, PKT_CMD_LENGTH);
        int idx = 0;
        short cmd = m_byteBuffer.getShort(idx);
        idx += PKT_CMD_LENGTH;

        // do processing
        boolean ret = false;
        if (cmd == PKT_CMD_ADD_DATA)
            ret = addData(pktBuf, idx);
        else if (cmd == PKT_CMD_QUERY_DATA)
            ret = queryData(pktBuf, idx);
        else if (cmd == PKT_CMD_RANGE_QUERY)
            ret = rangeQuery(pktBuf, idx);
        else if (cmd == PKT_CMD_QUERY_ALL)
            ret = queryAll(pktBuf, idx);
        else if (cmd == PKT_CMD_GET_MIN_TIME)
            ret = getMinimumTime(pktBuf, idx);
        else if (cmd == PKT_CMD_GET_STATISTICS)
            ret = getStatistics(pktBuf, idx);
        else if (cmd == PKT_CMD_DELETE)
            ret = deleteData(pktBuf, idx);
        else if (cmd == PKT_CMD_DELETE_ALL)
            ret = deleteAllData(pktBuf, idx);
        else
            m_log.error("Got unknown command: " + cmd);

        // stop measurement and log it
        m_cmdProcTimeObserver.stopAndLog("cmd: " + cmd);
        return ret;
    }

    private boolean putBufferWithCheckLength(byte[] pktBuf, int idx, int putLength) {
        if (pktBuf.length - idx < putLength) {
            m_log.error("packet length is too short: " + (pktBuf.length - idx) + ", expect: " + putLength
                    + ", idx: " + idx);
            return false;
        }
        m_byteBuffer.put(pktBuf, idx, putLength);
        return true;
    }

    private boolean addData(byte[] pktBuf, int idx) throws IOException {
        HistoryData history = new HistoryData();

        int putLength = PKT_DATA_TYPE_LENGTH + PKT_ID_LENGTH + PKT_SEC_LENGTH + PKT_NS_LENGTH;
        if (!putBufferWithCheckLength(pktBuf, idx, putLength)) {
            replyAddData(ErrorCode.PACKET_SHORT);
            return false;
        }

        // parse input parmeters
        history.type = m_byteBuffer.getShort(idx);
        idx += PKT_DATA_TYPE_LENGTH;

        history.id = m_byteBuffer.getLong(idx);
        idx += PKT_ID_LENGTH;

        history.sec = m_byteBuffer.getInt(idx);
        idx += PKT_SEC_LENGTH;

        history.ns = m_byteBuffer.getInt(idx);
        idx += PKT_NS_LENGTH;

        // parse each data
        int ret = ErrorCode.UNKNOWN_ERROR;
        if (history.type == HistoryData.TYPE_FLOAT)
            ret = procFloatData(pktBuf, idx, history);
        else if (history.type == HistoryData.TYPE_STRING)
            ret = procStringData(pktBuf, idx, history);
        else if (history.type == HistoryData.TYPE_UINT64)
            ret = procUint64Data(pktBuf, idx, history);
        else if (history.type == HistoryData.TYPE_BLOB)
            ret = procBlobData(pktBuf, idx, history);
        else {
            m_log.error("Got unknown data type: " + history.type);
            replyAddData(ErrorCode.INVALID_DATA_TYPE);
            return false;
        }
        if (ret != ErrorCode.SUCCESS) {
            replyAddData(ret);
            return false;
        }

        ret = m_driver.addData(history);
        if (ret != ErrorCode.SUCCESS) {
            replyAddData(ret);
            return true;
        }

        replyAddData(ErrorCode.SUCCESS);
        return true;
    }

    private boolean queryData(byte[] pktBuf, int idx) throws IOException {
        int putLength = PKT_ID_LENGTH + PKT_SEC_LENGTH + PKT_NS_LENGTH + PKT_QUERY_TYPE_LENGTH;
        if (!putBufferWithCheckLength(pktBuf, idx, putLength)) {
            replyQueryData(ErrorCode.PACKET_SHORT, null);
            return false;
        }

        // ID, sec0, and sec1
        long id = m_byteBuffer.getLong(idx);
        idx += PKT_ID_LENGTH;

        int sec = m_byteBuffer.getInt(idx);
        idx += PKT_SEC_LENGTH;

        int ns = m_byteBuffer.getInt(idx);
        idx += PKT_NS_LENGTH;

        short queryType = m_byteBuffer.getShort(idx);
        idx += PKT_QUERY_TYPE_LENGTH;

        HistoryData history = null;
        try {
            history = m_driver.queryData(id, sec, ns, queryType);
        } catch (HistoryDataSet.TooManyException e) {
            replyQueryData(ErrorCode.TOO_MANY_ENTRIES, history);
            return true;
        }
        if (history == null) {
            replyQueryData(ErrorCode.NOT_FOUND, history);
            return true;
        }

        replyQueryData(ErrorCode.SUCCESS, history);
        return true;
    }

    private boolean rangeQuery(byte[] pktBuf, int idx) throws IOException {
        int putLength = PKT_ID_LENGTH + (PKT_SEC_LENGTH + PKT_NS_LENGTH) * 2 + PKT_NUM_ENTRIES_LENGTH
                + PKT_SORT_ORDER_LENGTH;
        if (!putBufferWithCheckLength(pktBuf, idx, putLength)) {
            replyRangeQuery(ErrorCode.PACKET_SHORT, 0, (short) 0);
            return false;
        }

        // parse input parmeters
        long id = m_byteBuffer.getLong(idx);
        idx += PKT_ID_LENGTH;

        int sec0 = m_byteBuffer.getInt(idx);
        idx += PKT_SEC_LENGTH;

        int ns0 = m_byteBuffer.getInt(idx);
        idx += PKT_NS_LENGTH;

        int sec1 = m_byteBuffer.getInt(idx);
        idx += PKT_SEC_LENGTH;

        int ns1 = m_byteBuffer.getInt(idx);
        idx += PKT_NS_LENGTH;

        long maxEntries = m_byteBuffer.getLong(idx);
        idx += PKT_NUM_ENTRIES_LENGTH;

        short sortOrder = m_byteBuffer.getShort(idx);
        idx += PKT_SORT_ORDER_LENGTH;

        // Sort order
        if (sortOrder != PKT_SORT_ORDER_ASCENDING && sortOrder != PKT_SORT_ORDER_DESCENDING) {
            m_log.error("Unknown sort order: " + sortOrder);
            replyRangeQuery(ErrorCode.INVALID_SORT_ORDER, 0, sortOrder);
            return true;
        }

        // Get data
        HistoryDataSet dataSet = null;
        try {
            dataSet = m_driver.getData(id, sec0, ns0, sec1, ns1, maxEntries);
        } catch (HistoryDataSet.TooManyException e) {
            replyRangeQuery(ErrorCode.TOO_MANY_ENTRIES, 0, sortOrder);
            return true;
        }

        // Get sorted iterator
        Iterator<HistoryData> it = null;
        if (sortOrder == PKT_SORT_ORDER_ASCENDING)
            it = dataSet.iterator();
        else if (sortOrder == PKT_SORT_ORDER_DESCENDING)
            it = dataSet.descendingIterator();
        else {
            // This condition never happens, because it has been checked above.
        }

        // calculate entries
        long numEntries = dataSet.size();

        // write reply header to the socket
        replyRangeQuery(ErrorCode.SUCCESS, numEntries, sortOrder);

        // write quried data
        while (it.hasNext()) {
            HistoryData history = it.next();
            sendOneHistoryData(history);
        }
        m_ostream.flush();

        return true;
    }

    private boolean queryAll(byte[] pktBuf, int idx) throws IOException {
        try {
            // write reply header to the socket
            replyQueryAllHeader();

            // data loop
            BlockingQueue<HistoryStreamElement> queue = m_driver.openHistoryStream();
            int errorCode = ErrorCode.SUCCESS;
            while (true) {
                HistoryStreamElement streamElem = queue.take();
                if (streamElem.isEndOfStream()) {
                    errorCode = streamElem.getErrorCode();
                    break;
                }
                sendOneHistoryData(streamElem.getData());
                m_ostream.flush();
            }
            replyQueryAllFooter(errorCode);
        } catch (Exception e) {
            e.printStackTrace();
            m_log.error(e);
            replyQueryAllFooter(ErrorCode.UNKNOWN_ERROR);
            return true;
        } finally {
            m_driver.closeHistoryStream();
        }
        return true;
    }

    private boolean getMinimumTime(byte[] pktBuf, int idx) throws IOException {

        int putLength = PKT_ID_LENGTH;
        if (!putBufferWithCheckLength(pktBuf, idx, putLength)) {
            replyGetMinimumTime(ErrorCode.PACKET_SHORT, 0, 0);
            return false;
        }

        // get ID
        long id = m_byteBuffer.getLong(idx);
        idx += PKT_ID_LENGTH;

        // get boundary of sec
        HistoryData history = null;
        try {
            history = m_driver.getMinimumTime(id);
        } catch (HistoryDataSet.TooManyException e) {
            replyGetMinimumTime(ErrorCode.TOO_MANY_ENTRIES, 0, 0);
            return true;
        }

        if (history == null) {
            replyGetMinimumTime(ErrorCode.NOT_FOUND, 0, 0);
            return true;
        }

        replyGetMinimumTime(ErrorCode.SUCCESS, history.sec, history.ns);
        return true;
    }

    private boolean getStatistics(byte[] pktBuf, int idx) throws IOException {

        int putLength = PKT_ID_LENGTH + 2 * (PKT_SEC_LENGTH + PKT_NS_LENGTH);
        if (!putBufferWithCheckLength(pktBuf, idx, putLength)) {
            replyGetStatistics(ErrorCode.PACKET_SHORT, 0, 0, 0, 0, 0);
            return false;
        }

        // get ID and ts0, and ts1
        long id = m_byteBuffer.getLong(idx);
        idx += PKT_ID_LENGTH;

        int sec0 = m_byteBuffer.getInt(idx);
        idx += PKT_SEC_LENGTH;

        int ns0 = m_byteBuffer.getInt(idx);
        idx += PKT_NS_LENGTH;

        int sec1 = m_byteBuffer.getInt(idx);
        idx += PKT_SEC_LENGTH;

        int ns1 = m_byteBuffer.getInt(idx);
        idx += PKT_NS_LENGTH;

        Statistics statistics = null;
        try {
            statistics = m_driver.getStatistics(id, sec0, ns0, sec1, ns1);
        } catch (HistoryData.DataNotNumericException e) {
            m_log.error("Not value type: id: " + id);
            replyGetStatistics(ErrorCode.INVALID_DATA_TYPE, id, 0, 0, 0, 0);
            return true;
        } catch (HistoryDataSet.TooManyException e) {
            replyGetStatistics(ErrorCode.TOO_MANY_ENTRIES, id, 0, 0, 0, 0);
            return true;
        }
        if (statistics == null || statistics.count == 0) {
            replyGetStatistics(ErrorCode.NOT_FOUND, id, 0, 0, 0, 0);
            return true;
        }

        // write reply
        replyGetStatistics(ErrorCode.SUCCESS, id, statistics.count, statistics.min, statistics.max, statistics.sum);

        return true;
    }

    private boolean deleteData(byte[] pktBuf, int idx) throws IOException {

        int putLength = PKT_ID_LENGTH + PKT_SEC_LENGTH + PKT_NS_LENGTH + PKT_DELETE_WAY_LENGTH;
        ;
        if (!putBufferWithCheckLength(pktBuf, idx, putLength)) {
            replyDelete(ErrorCode.SUCCESS, 0);
            return false;
        }

        // get ID, sec, ns, and way
        long id = m_byteBuffer.getLong(idx);
        idx += PKT_ID_LENGTH;

        int sec = m_byteBuffer.getInt(idx);
        idx += PKT_SEC_LENGTH;

        int ns = m_byteBuffer.getInt(idx);
        idx += PKT_NS_LENGTH;

        short way = m_byteBuffer.getShort(idx);
        idx += PKT_DELETE_WAY_LENGTH;

        // get min sec
        long numDeleted = m_driver.delete(id, sec, ns, way);

        // write reply to the socket
        replyDelete(ErrorCode.SUCCESS, numDeleted);

        return true;
    }

    private boolean deleteAllData(byte[] pktBuf, int idx) throws IOException {
        int ret = m_driver.deleteAll();
        replyDeleteAll(ret);
        return true;
    }

    private int procFloatData(byte[] pktBuf, int idx, HistoryData history) {
        m_byteBuffer.clear();
        m_byteBuffer.put(pktBuf, idx, PKT_DATA_FLOAT_LENGTH);
        idx += PKT_DATA_FLOAT_LENGTH;
        history.dataFloat = m_byteBuffer.getDouble(0);
        return ErrorCode.SUCCESS;
    }

    private int procStringData(byte[] pktBuf, int idx, HistoryData history) throws IOException {
        m_byteBuffer.clear();
        m_byteBuffer.put(pktBuf, idx, PKT_DATA_STRING_SIZE_LENGTH);
        idx += PKT_DATA_STRING_SIZE_LENGTH;
        int stringLength = m_byteBuffer.getInt(0);

        // get string body
        byte[] bodyBuf = new byte[stringLength];
        if (m_istream.read(bodyBuf, 0, stringLength) == -1) {
            m_log.error("Unexpectedly input stream reaches the end.");
            return ErrorCode.IERR_READ_STREAM_END;
        }
        history.dataString = new String(bodyBuf);
        idx += stringLength;
        return ErrorCode.SUCCESS;
    }

    private int procUint64Data(byte[] pktBuf, int idx, HistoryData history) {
        m_byteBuffer.clear();
        m_byteBuffer.put(pktBuf, idx, PKT_DATA_UINT64_LENGTH);
        idx += PKT_DATA_UINT64_LENGTH;
        history.dataUint64 = m_byteBuffer.getLong(0);
        return ErrorCode.SUCCESS;
    }

    private int procBlobData(byte[] pktBuf, int idx, HistoryData history) throws IOException {
        m_byteBuffer.clear();
        m_byteBuffer.put(pktBuf, idx, PKT_DATA_BLOB_SIZE_LENGTH);
        idx += PKT_DATA_BLOB_SIZE_LENGTH;
        long blobLength = m_byteBuffer.getLong(0);

        // get string body
        if (blobLength > 0x7fffffff) {
            // TODO: large Blob support
            m_log.error("Current implementation hasn't supported the large size blob > 0x7fffffff.");
            return ErrorCode.NOT_IMPLEMENTED;
        }

        int _blobLength = (int) blobLength;
        byte[] bodyBuf = new byte[_blobLength];
        if (m_istream.read(bodyBuf, 0, _blobLength) == -1) {
            m_log.error("Unexpectedly input stream reaches the end.");
            return ErrorCode.IERR_READ_STREAM_END;
        }
        history.dataBlob = bodyBuf;
        idx += _blobLength;
        return ErrorCode.SUCCESS;
    }

    private int calcReplyPktSize(HistoryData history) {
        int size = 0;
        if (history.type == HistoryData.TYPE_FLOAT)
            size += PKT_DATA_FLOAT_LENGTH;
        else if (history.type == HistoryData.TYPE_STRING) {
            size += PKT_DATA_STRING_SIZE_LENGTH;
            size += history.dataString.length();
        } else if (history.type == HistoryData.TYPE_UINT64)
            size += PKT_DATA_UINT64_LENGTH;
        else if (history.type == HistoryData.TYPE_BLOB) {
            size += PKT_DATA_STRING_SIZE_LENGTH;
            size += history.dataBlob.length;
        } else
            m_log.error("Unknown history type:" + history.type);
        return size;
    }

    private void sendOneHistoryData(HistoryData history) throws IOException {
        m_byteBuffer.clear();
        m_byteBuffer.putLong(history.id);
        m_byteBuffer.putInt(history.sec);
        m_byteBuffer.putInt(history.ns);
        m_byteBuffer.putShort(history.type);

        if (history.type == HistoryData.TYPE_FLOAT)
            m_byteBuffer.putDouble(history.dataFloat);
        else if (history.type == HistoryData.TYPE_STRING)
            m_byteBuffer.putInt(history.dataString.length());
        else if (history.type == HistoryData.TYPE_UINT64)
            m_byteBuffer.putLong(history.dataUint64);
        else if (history.type == HistoryData.TYPE_BLOB)
            m_byteBuffer.putLong(history.dataBlob.length);
        else
            m_log.error("Unknown history type:" + history.type);

        // Send data (except string body and blob)
        m_ostream.write(m_byteBuffer.array(), 0, m_byteBuffer.position());

        // String body and Blob
        if (history.type == HistoryData.TYPE_STRING) {
            m_ostream.write(history.dataString.getBytes(), 0, history.dataString.length());
        } else if (history.type == HistoryData.TYPE_BLOB)
            m_ostream.write(history.dataBlob, 0, history.dataBlob.length);
    }

    //
    // reply methods
    //
    private void replyAddData(int errorCode) throws IOException {
        int length = PKT_CMD_LENGTH + REPLY_RESULT_LENGTH;
        m_byteBuffer.clear();
        m_byteBuffer.putInt(length);
        m_byteBuffer.putShort(PKT_CMD_ADD_DATA);
        m_byteBuffer.putInt(errorCode);
        m_ostream.write(m_byteBuffer.array(), 0, m_byteBuffer.position());
        m_ostream.flush();
    }

    private void replyQueryData(int errorCode, HistoryData history) throws IOException {
        int length = PKT_CMD_LENGTH + REPLY_RESULT_LENGTH;
        m_byteBuffer.clear();
        m_byteBuffer.putInt(length);
        m_byteBuffer.putShort(PKT_CMD_QUERY_DATA);
        m_byteBuffer.putInt(errorCode);
        m_ostream.write(m_byteBuffer.array(), 0, m_byteBuffer.position());
        if (history != null)
            sendOneHistoryData(history);
        m_ostream.flush();
    }

    private void replyRangeQuery(int errorCode, long numEntries, short sortOrder) throws IOException {
        int length = PKT_CMD_LENGTH + REPLY_RESULT_LENGTH + PKT_NUM_ENTRIES_LENGTH + PKT_SORT_ORDER_LENGTH;
        m_byteBuffer.clear();
        m_byteBuffer.putInt(length);
        m_byteBuffer.putShort(PKT_CMD_RANGE_QUERY);
        m_byteBuffer.putInt(errorCode);
        m_byteBuffer.putLong(numEntries);
        m_byteBuffer.putShort(sortOrder);
        m_ostream.write(m_byteBuffer.array(), 0, m_byteBuffer.position());
        m_ostream.flush();
    }

    private void replyQueryAllHeader() throws IOException {
        int length = PKT_CMD_LENGTH;
        m_byteBuffer.clear();
        m_byteBuffer.putInt(length);
        m_byteBuffer.putShort(PKT_CMD_QUERY_ALL);
        m_ostream.write(m_byteBuffer.array(), 0, m_byteBuffer.position());
        m_ostream.flush();
    }

    private void replyQueryAllFooter(int errorCode) throws IOException {
        long dummyId = 0;
        int dummySec = 0;
        int dummyNSec = 0;
        m_byteBuffer.clear();
        m_byteBuffer.putLong(dummyId);
        m_byteBuffer.putInt(dummySec);
        m_byteBuffer.putInt(dummyNSec);
        m_byteBuffer.putShort((short) STREAM_EVENT_TYPE_END);
        m_byteBuffer.putInt(errorCode);
        m_ostream.write(m_byteBuffer.array(), 0, m_byteBuffer.position());
        m_ostream.flush();
    }

    private void replyGetMinimumTime(int errorCode, int sec, int ns) throws IOException {
        int length = PKT_CMD_LENGTH + REPLY_RESULT_LENGTH + PKT_SEC_LENGTH + PKT_NS_LENGTH;
        ;
        m_byteBuffer.clear();
        m_byteBuffer.putInt(length);
        m_byteBuffer.putShort(PKT_CMD_GET_MIN_TIME);
        m_byteBuffer.putInt(errorCode);
        m_byteBuffer.putInt(sec);
        m_byteBuffer.putInt(ns);
        m_ostream.write(m_byteBuffer.array(), 0, m_byteBuffer.position());
        m_ostream.flush();
    }

    private void replyGetStatistics(int errorCode, long id, long count, double min, double max, double sum)
            throws IOException {
        int length = PKT_CMD_LENGTH + REPLY_RESULT_LENGTH + PKT_ID_LENGTH + PKT_DATA_FLOAT_LENGTH * 3
                + PKT_DATA_UINT64_LENGTH;
        m_byteBuffer.clear();
        m_byteBuffer.putInt(length);
        m_byteBuffer.putShort(PKT_CMD_GET_STATISTICS);
        m_byteBuffer.putInt(errorCode);
        m_byteBuffer.putLong(id);
        m_byteBuffer.putLong(count);
        m_byteBuffer.putDouble(min);
        m_byteBuffer.putDouble(max);
        m_byteBuffer.putDouble(sum);

        m_ostream.write(m_byteBuffer.array(), 0, m_byteBuffer.position());
        m_ostream.flush();
    }

    private void replyDelete(int errorCode, long numDeleted) throws IOException {
        int length = PKT_CMD_LENGTH + REPLY_RESULT_LENGTH + PKT_NUM_DELETED_LENGTH;
        m_byteBuffer.clear();
        m_byteBuffer.putInt(length);
        m_byteBuffer.putShort(PKT_CMD_DELETE);
        m_byteBuffer.putInt(ErrorCode.SUCCESS);
        m_byteBuffer.putLong(numDeleted);
        m_ostream.write(m_byteBuffer.array(), 0, m_byteBuffer.position());
        m_ostream.flush();
    }

    private void replyDeleteAll(int errorCode) throws IOException {
        int length = PKT_CMD_LENGTH + REPLY_RESULT_LENGTH;
        m_byteBuffer.clear();
        m_byteBuffer.putInt(length);
        m_byteBuffer.putShort(PKT_CMD_DELETE_ALL);
        m_byteBuffer.putInt(errorCode);
        m_ostream.write(m_byteBuffer.array(), 0, m_byteBuffer.position());
        m_ostream.flush();
    }
}