com.portfolioeffect.quant.client.ClientConnection.java Source code

Java tutorial

Introduction

Here is the source code for com.portfolioeffect.quant.client.ClientConnection.java

Source

/*
 * #%L
 * PortfolioEffect - Quant Client
 * %%
 * Copyright (C) 2011 - 2015 Snowfall Systems, Inc.
 * %%
 * This file is part of PortfolioEffect Quant Client.
 * 
 * PortfolioEffect Quant Client 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.
 * 
 * PortfolioEffect Quant Client 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 PortfolioEffect Quant Client. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */
package com.portfolioeffect.quant.client;

import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.commons.lang.SerializationUtils;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.iq80.snappy.Snappy;
import org.openfast.Context;
import org.openfast.Message;
import org.openfast.MessageInputStream;
import org.openfast.MessageOutputStream;
import org.openfast.error.FastException;
import org.openfast.examples.MessageBlockReaderFactory;
import org.openfast.examples.MessageBlockWriterFactory;
import org.openfast.examples.OpenFastExample.Variant;
import org.openfast.session.Connection;
import org.openfast.session.Endpoint;
import org.openfast.session.FastConnectionException;
import org.openfast.session.tcp.TcpEndpoint;
import org.openfast.template.TemplateRegistry;
import org.openfast.template.loader.XMLMessageTemplateLoader;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.portfolioeffect.quant.client.message.CalculationStatusMessage;
import com.portfolioeffect.quant.client.message.ClientMessage;
import com.portfolioeffect.quant.client.message.LogoutResponse;
import com.portfolioeffect.quant.client.message.NonparametricComputeResponse;
import com.portfolioeffect.quant.client.message.Reject;
import com.portfolioeffect.quant.client.message.ServiceMessage;
import com.portfolioeffect.quant.client.message.TestRequest;
import com.portfolioeffect.quant.client.message.TransmitDataListMessage;
import com.portfolioeffect.quant.client.message.TransmitDataRequest;
import com.portfolioeffect.quant.client.message.TransmitDataResponse;
import com.portfolioeffect.quant.client.message.ValidationResponse;
import com.portfolioeffect.quant.client.message.type.EncryptMethodType;
import com.portfolioeffect.quant.client.message.type.EncryptedPasswordMethodType;
import com.portfolioeffect.quant.client.message.type.FastMessageType;
import com.portfolioeffect.quant.client.message.util.ClientRequestMessageFactory;
import com.portfolioeffect.quant.client.message.util.CryptograhicUtils;
import com.portfolioeffect.quant.client.message.util.ServerResponseMessageFactory;
import com.portfolioeffect.quant.client.message.util.ServerResponseMessageParser;
import com.portfolioeffect.quant.client.model.ConnectFailedException;
import com.portfolioeffect.quant.client.model.PriceDataSet;
import com.portfolioeffect.quant.client.portfolio.ArrayCache;
import com.portfolioeffect.quant.client.portfolio.ArrayCacheType;
import com.portfolioeffect.quant.client.result.MethodResult;
import com.portfolioeffect.quant.client.util.Console;
import com.portfolioeffect.quant.client.util.MessageStrings;
import com.portfolioeffect.quant.client.util.ProgressBar;
import com.portfolioeffect.quant.client.util.StopWatch;

public class ClientConnection {

    private static final int RESTART_ATTEMPTS_COUNT = 5;
    private static final int TIME_WAIT_TOPRINT = 30;
    private static final String SUPPORTED_CHARSET = "US-ASCII";
    private static final int TEST_PORT_NUMBER = 3443;
    private static final int MAX_BLOCK_DIMENSION = 100000;
    private static final int BLOCK_DIMENSION_DECREASE_STEP = 10;
    private static final int USER_LAYER_TIMEOUT_SECONDS_ESTIMATE = 60 * 5;
    private static final int DATA_TRANSMIT_TIMEOUT_SECONDS_ESTIMATE = 60 * 5;
    private static final int LOGON_TIMEOUT_SECONDS = 30;
    private static final int SERVICE_TIMEOUT_SEC = 30;
    private static final int PORT_NUMBER = 443;
    private static final String TEMPLATES_FILE = "config/template-quant.xml";
    private static final int LOGON_ATTEMPT_COUNT = 9;
    private static final int DEFAULT_LOGON_TIMEOUT_SEC = 30;
    private static final int HEARTBEAT_INTERVAL = 30;
    private static final EncryptMethodType ENCRYPT_METHOD_TYPE = EncryptMethodType.NONE;

    private String apiKey;
    private String username;
    private String password;
    private String templatesFileName;
    private String host;
    private int port;
    private Connection connection;
    private MessageOutputStream out;
    private MessageInputStream in;
    private MessageBlockWriterFactory messageBlockWriterFactory;
    private MessageBlockReaderFactory messageBlockReaderFactory;
    private volatile boolean isLoggedOn = false;
    private boolean isConnected = false;
    private boolean isMessageLoggingEnabled = true;
    private Logger logger = Logger.getLogger(this.getClass());
    private Thread inboundMessageRouter;
    private Endpoint endpoint;
    private TemplateRegistry templateRegistry;
    private int outboundMsgSeqNum;
    private int indicatorDefRequestNum;
    private volatile LinkedBlockingDeque<ClientMessage> clientMessageQueue;
    private volatile LinkedBlockingDeque<ServiceMessage> serviceMessageQueue;
    private StopWatch timeDataFast = new StopWatch();
    private StopWatch timeDataTransmit = new StopWatch();
    private boolean debugModeEnabled = false;
    private StringBuffer callStatus = new StringBuffer();
    private int groupSize = 1;
    private int progressBarI = 0;
    private int progressBarMax = 0;
    private Thread heartbeatMonitor;
    private ProgressBar progressBar = new ProgressBar();

    private long idClient;

    private static AtomicLong idClientGenerator;

    private static AtomicLong id;

    static {
        id = new AtomicLong();
        idClientGenerator = new AtomicLong();

    }

    public static long getNewId() {
        return id.incrementAndGet();
    }

    public long getIdClient() {
        return idClient;
    }

    public ClientConnection() {
        setMessageLoggingEnabled(false);
        setTemplatesFileName(TEMPLATES_FILE);
        setPort(PORT_NUMBER);
        idClient = idClientGenerator.getAndIncrement();

    }

    public long getIdC() {
        return idClient;
    }

    /**
     * Establishes connection to the server. Should be followed by the a call to
     * logon(), otherwise client will be disconneted shortly.
     * 
     * @throws IOException
     * @throws FastConnectionException
     */
    public void start() throws IOException, FastConnectionException {

        logger.setLevel(Level.ERROR);

        // load connection properties from persistent storage
        endpoint = new TcpEndpoint(host, port);

        //create XML message loader template and populate its parameters
        XMLMessageTemplateLoader loader = new XMLMessageTemplateLoader();
        loader.setLoadTemplateIdFromAuxId(true);

        loader.load(Thread.currentThread().getContextClassLoader().getResourceAsStream(templatesFileName));

        // get template registry from message loader and create opefast context
        templateRegistry = loader.getTemplateRegistry();
        Context context = new Context();
        context.setTemplateRegistry(templateRegistry);

        // create message block reader & writer 
        messageBlockWriterFactory = new MessageBlockWriterFactory(Variant.DEFAULT, 0, false);
        messageBlockReaderFactory = new MessageBlockReaderFactory(Variant.DEFAULT, 0, false);

        // create connection
        connection = endpoint.connect();

        // create input stream
        in = new MessageInputStream(connection.getInputStream(), context);
        in.setBlockReader(messageBlockReaderFactory.create());

        // create output stream
        out = new MessageOutputStream(connection.getOutputStream(), context);
        out.setBlockWriter(messageBlockWriterFactory.create());

        isConnected = true;

        logger.info("Client connected to endpoint " + endpoint);

        clientMessageQueue = new LinkedBlockingDeque<ClientMessage>();
        serviceMessageQueue = new LinkedBlockingDeque<ServiceMessage>();

        inboundMessageRouter = new Thread(new InboundMessageWorker());
        inboundMessageRouter.start();

        heartbeatMonitor = new Thread(new HeartbeatMonitor());
        heartbeatMonitor.start();

    }

    /**
     * Closes existing open connection to the server.
     */
    public void stop() {

        if (isConnected) {
            try {
                if (isLoggedOn()) {
                    logout(LOGON_TIMEOUT_SECONDS);
                }
                endpoint.close();
                connection.close();

                inboundMessageRouter.join();

                serviceMessageQueue.offer(new ServiceMessage(System.currentTimeMillis(), true));
                // heartbeatMonitor.join();//it dont stop
                heartbeatMonitor.interrupt();

            } catch (Exception e) {
                throw new RuntimeException(MessageStrings.ERROR_STOP_CLIENT, e);
            }

            isConnected = false;
        }

    }

    public void logout() {
        if (isConnected && isLoggedOn()) {
            Message logoutMsg = ClientRequestMessageFactory.createLogoutRequest(templateRegistry,
                    getOutboundMsgSequenceNumber(), System.currentTimeMillis());
            out.writeMessage(logoutMsg);
        }
    };

    /**
     * Request client logon with credentials provided using setter methods.
     * 
     * @throws Exception
     */

    public void logon() {
        try {
            logon(DEFAULT_LOGON_TIMEOUT_SEC);
        } catch (Exception e) {
            stop();
            // Console.writeStackTrace(e);
        }
    };

    public void logon(int timeoutSec) throws Exception {
        logon(timeoutSec, 0);
    }

    public boolean logon(int timeoutSec, int attemptCount) throws Exception {
        if (attemptCount > LOGON_ATTEMPT_COUNT)
            return false;

        String encryptedPassword = CryptograhicUtils.encrypt(password, apiKey);
        Message loginMsg = ClientRequestMessageFactory.createLogonRequest(templateRegistry, HEARTBEAT_INTERVAL,
                ENCRYPT_METHOD_TYPE, username, encryptedPassword, EncryptedPasswordMethodType.AES,
                encryptedPassword.length(), getOutboundMsgSequenceNumber(), System.currentTimeMillis());

        Message msg = sendAndAwaitResponse(loginMsg, timeoutSec);
        FastMessageType responseMessageType = getMessageType(msg);

        String curentHost = getHost();
        int curentPort = getPort();

        if (responseMessageType == FastMessageType.LOGOUT) {
            LogoutResponse logoutReponse = ServerResponseMessageParser.parseLogoutResponse(msg);
            throw new Exception(logoutReponse.getText());
        }

        setHost(curentHost);
        setPort(curentPort);

        if (attemptCount == 0 && !isLoggedOn()) {
            throw new ConnectFailedException();

        }

        return isLoggedOn;
    }

    /**
     * Request client logout
     */
    public void logout(int timeoutSec) {
        if (!isLoggedOn())
            return;
        Message logoutMsg = ClientRequestMessageFactory.createLogoutRequest(templateRegistry,
                getOutboundMsgSequenceNumber(), System.currentTimeMillis());
        try {
            sendAndAwaitResponse(logoutMsg, timeoutSec);
        } catch (Exception e) {
            isLoggedOn = false;
        }
    }

    public void progressBarIAdd(int value) {
        if (progressBarMax == 0) {
            progressBarMax = value * groupSize;
            progressBarI = 0;
        }
        progressBar.printCompletionStatus(progressBarI, progressBarMax);
        progressBarI += value;
        progressBar.printCompletionStatus(progressBarI, progressBarMax);
        if (progressBarI == progressBarMax) {
            createCallGroup(1);
        }
    }

    public void createCallGroup(int groupSize) {
        this.groupSize = groupSize;
        this.progressBarI = 0;
        this.progressBarMax = 0;
        progressBar.reset();
        progressBar.setScale(groupSize);

    }

    public void printProgressBar(double percent) {
        progressBar.printCompletionStatus(percent);
    }

    public void proggressBarOn() {
        progressBar.setON(true);
    }

    public void proggressBarOff() {
        progressBar.setON(false);
    }

    public void resetProgressBar() {
        progressBar.reset();
    }

    public void setHost(String host) {
        this.host = host;
        if (host.equals("localhost"))
            setPort(TEST_PORT_NUMBER);
        else
            setPort(PORT_NUMBER);
    }

    public MethodResult start(String username, String password, String apiKey, String remoteHostName) {

        clearStatus();
        stop();

        setMessageLoggingEnabled(false);
        setTemplatesFileName(TEMPLATES_FILE);

        if (remoteHostName.equals("localhost"))
            setPort(TEST_PORT_NUMBER);
        else
            setPort(PORT_NUMBER);

        setUsername(username);
        setPassword(password);
        setApiKey(apiKey);
        setHost(remoteHostName);

        try {
            start();
        } catch (Exception e) {
            this.stop();
            return new MethodResult(MessageStrings.ERROR_CONNECT);
        }
        try {
            logon(LOGON_TIMEOUT_SECONDS);
        } catch (Exception e) {
            stop();
            if (e.getMessage().contains(":"))
                return new MethodResult(e.getMessage().split(":")[1]);
            else
                return new MethodResult(e.getMessage());
        }

        return new MethodResult();
    }

    private void clearStatus() {
        callStatus.setLength(0);
    }

    public MethodResult restart() {
        int totalTime = 0;

        for (int i = 1; i <= RESTART_ATTEMPTS_COUNT; i++) {
            stop();
            try {
                if (i > 1) {
                    int waitTime = (int) (i * (5 + Math.random() * 2));
                    totalTime += waitTime;
                    if (totalTime > TIME_WAIT_TOPRINT)
                        Console.write(MessageStrings.CONNECTING);

                    waitAndDots(waitTime, totalTime);

                }

            } catch (InterruptedException e) {
            }

            clearStatus();

            try {
                start();
                logon(LOGON_TIMEOUT_SECONDS);
            } catch (IOException e) {
                continue;
            } catch (FastConnectionException e) {
                continue;
            } catch (ConnectFailedException e) {
                continue;
            } catch (Exception e) {
                stop();
                return new MethodResult(e.getMessage());
            }
            if (isLoggedOn()) {
                if (totalTime > TIME_WAIT_TOPRINT)
                    Console.writeln(MessageStrings.OK);

                return new MethodResult();
            }

        }
        if (!isLoggedOn()) {
            return new MethodResult(MessageStrings.ERROR_CONNECT);
        }
        return new MethodResult();

    }

    private void waitAndDots(int sec, int totalTime) throws InterruptedException {
        if (totalTime > TIME_WAIT_TOPRINT) {
            Console.write(".");
        }
        Thread.sleep(sec);
    }

    public double getDataVolume(double[] price, int[] timeSec) {
        PriceDataSet data;
        try {
            data = new PriceDataSet(price, timeSec);
        } catch (Exception e) {
            return 0;
        }
        double a = data.toBinaryZipCompress().length;
        return a / 1024 / 1024;
    }

    public double getDataVolume(double[] price) {
        return getDataVolume(price, new int[0]);
    }

    public static boolean isPureAscii(String v) {
        CharsetEncoder asciiEncoder = Charset.forName(SUPPORTED_CHARSET).newEncoder();
        return asciiEncoder.canEncode(v);
    }

    public MethodResult validateStringRequest(String requestString) throws Exception {

        if (!isPureAscii(requestString))
            return new MethodResult(MessageStrings.NON_ASCII);

        String[] paramList = new String[0];

        Message msg = ClientRequestMessageFactory.createValidationRequest(getTemplateRegistry(), requestString,
                getOutboundMsgSequenceNumber(), System.currentTimeMillis());
        Message responseMsg = sendAndAwaitResponse(msg, USER_LAYER_TIMEOUT_SECONDS_ESTIMATE);

        ValidationResponse response = ServerResponseMessageParser.parseValidationResponse(responseMsg);
        if (response.getMsgType().equals("OK")) {

            Type mapType = new TypeToken<String[]>() {
            }.getType();
            Gson gson = new Gson();

            paramList = gson.fromJson(response.getMsgBody(), mapType);
        } else {
            return new MethodResult(response.getMsgBody());
        }

        MethodResult result = new MethodResult();
        ArrayCache pL = new ArrayCache(paramList);
        result.setData("positions", pL);
        return result;
    }

    public MethodResult callEstimator(String estimatorType, double[] price, int timeStep) throws Exception {
        int[] timeSec = new int[price.length];
        for (int i = 0; i < price.length; i++)
            timeSec[i] = i * timeStep + 1;
        return callEstimator(estimatorType, price, timeSec);
    }

    public MethodResult transmitHistoryPrice(String assetName) throws Exception {

        String requestType = "HISTORY_PRICE";
        String request = assetName;

        Message msg = ServerResponseMessageFactory.createTransmitDataRequest(getTemplateRegistry(), requestType,
                request, getOutboundMsgSequenceNumber(), System.currentTimeMillis());
        Message responseMsg = sendAndAwaitResponse(msg, DATA_TRANSMIT_TIMEOUT_SECONDS_ESTIMATE);
        TransmitDataResponse response = ServerResponseMessageParser.parseTransmitDataResponse(responseMsg);

        if (response.getMsgType().equals("OK")) {

            MethodResult result = new MethodResult();
            result.setMessage(response.getMsgBody());

            return result;

        } else {
            throw new Exception(response.getMsgBody());
        }

    }

    public MethodResult transmitQuantity(String assetName, int[] dataInt, long[] time) throws Exception {

        boolean isFirstBlock = true;
        int position = 0;

        for (int i = 0; i < time.length / MAX_BLOCK_DIMENSION; i++) {
            int[] dataTransmit = new int[MAX_BLOCK_DIMENSION];
            long[] timeTransmit = new long[MAX_BLOCK_DIMENSION];

            System.arraycopy(time, position, timeTransmit, 0, MAX_BLOCK_DIMENSION);
            System.arraycopy(dataInt, position, dataTransmit, 0, MAX_BLOCK_DIMENSION);

            String type = "QUANTITY";
            if (!isFirstBlock) {
                type += ":+";
            } else
                isFirstBlock = false;

            String request = assetName;
            Message msg = ServerResponseMessageFactory.createTransmitDataRequest(getTemplateRegistry(), type,
                    request, dataTransmit, timeTransmit, getOutboundMsgSequenceNumber(),
                    System.currentTimeMillis());

            Message responseMsg = sendAndAwaitResponse(msg, DATA_TRANSMIT_TIMEOUT_SECONDS_ESTIMATE);

            TransmitDataResponse response = ServerResponseMessageParser.parseTransmitDataResponse(responseMsg);

            position += MAX_BLOCK_DIMENSION;

            if (!response.getMsgType().equals("OK"))
                throw new Exception(response.getMsgBody());

        }

        if (time.length % MAX_BLOCK_DIMENSION != 0) {
            int[] dataTransmit = new int[time.length % MAX_BLOCK_DIMENSION];
            long[] timeTransmit = new long[time.length % MAX_BLOCK_DIMENSION];

            System.arraycopy(time, position, timeTransmit, 0, time.length % MAX_BLOCK_DIMENSION);
            System.arraycopy(dataInt, position, dataTransmit, 0, time.length % MAX_BLOCK_DIMENSION);

            String type = "QUANTITY";
            if (!isFirstBlock) {
                type += ":+";
            } else
                isFirstBlock = false;

            String request = assetName;
            Message msg = ServerResponseMessageFactory.createTransmitDataRequest(getTemplateRegistry(), type,
                    request, dataTransmit, timeTransmit, getOutboundMsgSequenceNumber(),
                    System.currentTimeMillis());

            Message responseMsg = sendAndAwaitResponse(msg, DATA_TRANSMIT_TIMEOUT_SECONDS_ESTIMATE);

            TransmitDataResponse response = ServerResponseMessageParser.parseTransmitDataResponse(responseMsg);

            if (!response.getMsgType().equals("OK"))
                throw new Exception(response.getMsgBody());

        }

        MethodResult result = new MethodResult();
        result.setMessage("NON");

        return result;
    }

    public MethodResult transmitUserPrice(String assetName, float[] dataFloat, long[] time) throws Exception {

        boolean isFirstBlock = true;
        int position = 0;

        for (int i = 0; i < time.length / MAX_BLOCK_DIMENSION; i++) {
            float[] dataTransmit = new float[MAX_BLOCK_DIMENSION];
            long[] timeTransmit = new long[MAX_BLOCK_DIMENSION];

            System.arraycopy(time, position, timeTransmit, 0, MAX_BLOCK_DIMENSION);
            System.arraycopy(dataFloat, position, dataTransmit, 0, MAX_BLOCK_DIMENSION);

            String type = "USER_PRICE";
            if (!isFirstBlock) {
                type += ":+";
            } else {
                isFirstBlock = false;
            }

            String request = assetName;
            Message msg = ServerResponseMessageFactory.createTransmitDataRequest(getTemplateRegistry(), type,
                    request, dataTransmit, timeTransmit, getOutboundMsgSequenceNumber(),
                    System.currentTimeMillis());

            Message responseMsg = sendAndAwaitResponse(msg, DATA_TRANSMIT_TIMEOUT_SECONDS_ESTIMATE);

            TransmitDataResponse response = ServerResponseMessageParser.parseTransmitDataResponse(responseMsg);

            position += MAX_BLOCK_DIMENSION;

            if (!response.getMsgType().equals("OK"))
                throw new Exception(response.getMsgBody());

        }

        if (time.length % MAX_BLOCK_DIMENSION != 0) {

            float[] dataTransmit = new float[time.length % MAX_BLOCK_DIMENSION];
            long[] timeTransmit = new long[time.length % MAX_BLOCK_DIMENSION];

            System.arraycopy(time, position, timeTransmit, 0, time.length % MAX_BLOCK_DIMENSION);
            System.arraycopy(dataFloat, position, dataTransmit, 0, time.length % MAX_BLOCK_DIMENSION);

            String type = "USER_PRICE";
            if (!isFirstBlock) {
                type += ":+";
            } else
                isFirstBlock = false;

            String request = assetName;
            Message msg = ServerResponseMessageFactory.createTransmitDataRequest(getTemplateRegistry(), type,
                    request, dataTransmit, timeTransmit, getOutboundMsgSequenceNumber(),
                    System.currentTimeMillis());

            Message responseMsg = sendAndAwaitResponse(msg, DATA_TRANSMIT_TIMEOUT_SECONDS_ESTIMATE);

            TransmitDataResponse response = ServerResponseMessageParser.parseTransmitDataResponse(responseMsg);

            if (!response.getMsgType().equals("OK"))
                throw new Exception(response.getMsgBody());

        }

        MethodResult result = new MethodResult();
        result.setMessage("NON");

        return result;

    }

    public MethodResult transmitDataList(String fromTime, String toTime, ArrayList<String> dataList,
            String windowLength, String priceSamplingInterval, String momentsModel) throws Exception {
        timeDataFast.reset();
        timeDataTransmit.reset();

        String requestType = "CHECK_DATA";

        TransmitDataListMessage dataListMessage = new TransmitDataListMessage(dataList, windowLength, fromTime,
                toTime, priceSamplingInterval, momentsModel);

        Gson gson = new Gson();
        Type mapType = new TypeToken<TransmitDataListMessage>() {
        }.getType();
        String request = gson.toJson(dataListMessage, mapType);

        timeDataFast.start();
        Message msg = ServerResponseMessageFactory.createTransmitDataRequest(getTemplateRegistry(), requestType,
                request, getOutboundMsgSequenceNumber(), System.currentTimeMillis());
        timeDataFast.stop();
        timeDataTransmit.start();
        Message responseMsg = sendAndAwaitResponse(msg, DATA_TRANSMIT_TIMEOUT_SECONDS_ESTIMATE);

        timeDataTransmit.stop();
        timeDataFast.start();
        TransmitDataResponse response = ServerResponseMessageParser.parseTransmitDataResponse(responseMsg);
        timeDataFast.stop();

        if (response.getMsgType().equals("OK")) {

            MethodResult result = new MethodResult();
            result.setMessage(response.getMsgBody());

            return result;

        } else {
            return new MethodResult(response.getMsgBody());
        }
    }

    public MethodResult callEstimator(String estimatorType, double[] price, int[] timeSec) {
        double[] result = null;

        estimatorType = "[" + estimatorType + "]";

        if (progressBarMax == 0) {
            progressBarMax = price.length * groupSize;
            progressBarI = 0;
            progressBar.reset();
        }
        if (progressBarMax != 0 && progressBarI == 0) {
            progressBar.reset();
        }

        int curentBlockDimension = MAX_BLOCK_DIMENSION * BLOCK_DIMENSION_DECREASE_STEP;
        if (!isLoggedOn()) {
            curentBlockDimension *= BLOCK_DIMENSION_DECREASE_STEP;
        }
        while (true) {
            curentBlockDimension /= BLOCK_DIMENSION_DECREASE_STEP;
            if (curentBlockDimension < 2)
                return new MethodResult(MessageStrings.SERVER_TIME_OUT);

            result = null;
            progressBar.printCompletionStatus(progressBarI, progressBarMax);

            if (price.length == 0) {
                progressBarMax = 0;
                return new MethodResult(MessageStrings.WRONG_VECTOR_LEN_PRICE);
            }

            if (timeSec.length != price.length && timeSec.length != 0) {
                progressBarMax = 0;
                return new MethodResult(MessageStrings.WRONG_VECTOR_LEN_PRICE_AND_TIME);
            }

            if (timeSec.length == 0) {
                timeSec = new int[price.length];
                for (int i = 0; i < price.length; i++)
                    timeSec[i] = i + 1;
            }

            int maxBlockDimension = curentBlockDimension;// / price.length;
            double ttt = price.length;
            ttt = ttt / maxBlockDimension + 0.5;
            int numberBlocks = (int) ttt;
            int pointer = 0;

            for (int i = 0; i < numberBlocks - 1; i++) {
                double[] priceBlock = new double[maxBlockDimension];
                int[] timeSecBlock;
                timeSecBlock = new int[maxBlockDimension];
                PriceDataSet data;
                {
                    if (timeSec.length != price.length) {
                        progressBarMax = 0;
                        return new MethodResult(MessageStrings.WRONG_VECTOR_LEN_PRICE_AND_TIME);
                    }
                    for (int j = 0, localPointer = pointer; j < maxBlockDimension; j++, localPointer++) {
                        priceBlock[j] = price[localPointer];
                        timeSecBlock[j] = timeSec[localPointer];
                    }

                    try {
                        data = new PriceDataSet(priceBlock, timeSecBlock);
                    } catch (Exception e) {
                        return new MethodResult(e.getMessage());
                    }
                }

                double[] resultBlock = new double[] {};

                try {
                    clearStatus();
                    Message msg = ClientRequestMessageFactory.createNonparametricComputeRequest(
                            getTemplateRegistry(), estimatorType, "false", priceBlock, timeSecBlock,
                            getOutboundMsgSequenceNumber(), System.currentTimeMillis());

                    Message responseMsg = sendAndAwaitResponse(msg, USER_LAYER_TIMEOUT_SECONDS_ESTIMATE);

                    NonparametricComputeResponse response = ServerResponseMessageParser
                            .parseNonparametricComputeResponse(responseMsg);

                    if (response.getMsgType().equals("OK")) {
                        resultBlock = response.getData();
                    } else {
                        throw new Exception(response.getMsgBody());
                    }

                }

                catch (ConnectFailedException e) {

                    MethodResult isRestarted = restart();
                    if (isRestarted.hasError()) {
                        return new MethodResult(isRestarted.getErrorMessage());
                    }
                    numberBlocks = -1;
                    continue;

                }

                catch (Exception e) {

                    return new MethodResult(e.getMessage());

                }

                if (result == null) {
                    result = new double[price.length];
                }

                {

                    for (int j = 0, localPointer = pointer; j < resultBlock.length; j++, localPointer++)
                        result[localPointer] = resultBlock[j];

                }

                pointer += maxBlockDimension;

                progressBarI += resultBlock.length;
                progressBar.printCompletionStatus(progressBarI, progressBarMax);

            }

            if (numberBlocks == -1)
                continue;

            // This is last Block !!!!!!!!!!!!!!!!!

            double[] priceBlock = new double[price.length - pointer];
            int[] timeSecBlock;
            timeSecBlock = new int[price.length - pointer];

            PriceDataSet data;

            {

                if (timeSec.length != price.length) {
                    progressBarMax = 0;
                    return new MethodResult(MessageStrings.WRONG_VECTOR_LEN_PRICE_AND_TIME);

                }

                for (int j = 0, localPointer = pointer; j < price.length - pointer; j++, localPointer++) {
                    priceBlock[j] = price[localPointer];
                    timeSecBlock[j] = timeSec[localPointer];
                }

                try {
                    data = new PriceDataSet(priceBlock, timeSecBlock);
                } catch (Exception e) {

                    return new MethodResult(e.getMessage());

                }
            }

            double[] resultBlock = new double[] {};

            try {

                Message msg = ClientRequestMessageFactory.createNonparametricComputeRequest(getTemplateRegistry(),
                        estimatorType, "true", priceBlock, timeSecBlock, getOutboundMsgSequenceNumber(),
                        System.currentTimeMillis());

                Message responseMsg = sendAndAwaitResponse(msg, USER_LAYER_TIMEOUT_SECONDS_ESTIMATE);

                NonparametricComputeResponse response = ServerResponseMessageParser
                        .parseNonparametricComputeResponse(responseMsg);

                if (response.getMsgType().equals("OK")) {

                    resultBlock = response.getData();
                }

                else {

                    throw new Exception(response.getMsgBody());
                }

            } catch (ConnectFailedException e) {

                MethodResult isRestarted = restart();
                if (isRestarted.hasError()) {
                    return new MethodResult(isRestarted.getErrorMessage());
                }
                numberBlocks = -1;
                continue;

            }

            catch (Exception e) {

                return new MethodResult(e.getMessage());

            }

            if (result == null) {
                result = new double[price.length];
            }

            {

                for (int j = 0, localPointer = pointer; j < resultBlock.length; j++, localPointer++)
                    result[localPointer] = resultBlock[j];

            }

            progressBarI += resultBlock.length;
            progressBar.printCompletionStatus(progressBarI, progressBarMax);

            if (progressBarI == progressBarMax) {
                createCallGroup(1);
            }

            break;
        }

        ArrayCache resultCache;
        ArrayCache resultTime;
        try {
            resultCache = new ArrayCache(result);

            resultTime = new ArrayCache(timeSec);

        } catch (IOException e) {
            return new MethodResult(e.getMessage());

        }

        MethodResult resultA = new MethodResult();
        resultA.setData("value", resultCache);
        resultA.setData("time", resultTime);

        return resultA;
    }

    public MethodResult estimateEstimator(String metricType) throws Exception {

        HashMap<String, String> info = new HashMap<String, String>();
        ArrayCache resultValueList = null;
        ArrayCache resultTimeList = null;

        boolean isRun = true;
        boolean isFirstBlock = true;
        double percent = 0;

        int[] dimensions = null;
        progressBar.printCompletionStatus(percent);

        while (isRun) {

            clearStatus();
            Message responseMsg;
            if (isFirstBlock) {

                resultValueList = new ArrayCache(ArrayCacheType.DOUBLE_VECTOR);
                resultTimeList = new ArrayCache(ArrayCacheType.LONG_VECTOR);

                //Gson gson = new Gson();

                Message msg = ClientRequestMessageFactory.createNonparametricComputeRequest(getTemplateRegistry(),
                        metricType, "", new double[1], new int[1], getOutboundMsgSequenceNumber(),
                        System.currentTimeMillis());

                responseMsg = sendAndAwaitResponse(msg, USER_LAYER_TIMEOUT_SECONDS_ESTIMATE);

                isFirstBlock = false;
            } else {

                Message msg = ClientRequestMessageFactory.createNonparametricComputeRequest(getTemplateRegistry(),
                        metricType, "#NEXT#", new double[1], new int[1], getOutboundMsgSequenceNumber(),
                        System.currentTimeMillis());

                responseMsg = sendAndAwaitResponse(msg, USER_LAYER_TIMEOUT_SECONDS_ESTIMATE);

            }

            TransmitDataRequest response = ServerResponseMessageParser.parseTransmitDataRequest(responseMsg);

            if (response.getMsgType().contains("OK")) {

                Gson gson = new Gson();
                Type mapType = new TypeToken<CalculationStatusMessage>() {
                }.getType();

                CalculationStatusMessage statusMessg = gson.fromJson(response.getMsgType(), mapType);

                dimensions = statusMessg.getDimension();

                float[] data = response.getDataFloat();
                long[] time = response.getTime();

                resultValueList.writeAsDouble(data);

                resultTimeList.write(time);

                percent = Double.valueOf(response.getMsgBody());
                progressBar.printCompletionStatus(percent);

                if (response.getMsgType().contains("STOP")) {

                    info = statusMessg.getResultInfo();

                    isRun = false;
                }
            } else {

                throw new Exception(response.getMsgBody());
            }
        }

        if (dimensions != null)
            resultValueList.setDimensions(dimensions);

        MethodResult result = new MethodResult();
        result.setData("value", resultValueList);
        result.setData("time", resultTimeList);
        result.setInfo(info);

        return result;
    }

    public MethodResult estimateTransactional(String metricType, String indexPosition,
            ArrayList<String> positionList, String params) throws Exception {

        HashMap<String, String> info = new HashMap<String, String>();
        ArrayCache resultValueList = null;
        ArrayCache resultTimeList = null;

        boolean isRun = true;
        boolean isFirstBlock = true;
        double percent = 0;

        int[] dimensions = null;
        progressBar.printCompletionStatus(percent);

        while (isRun) {

            clearStatus();
            Message responseMsg;
            if (isFirstBlock) {

                resultValueList = new ArrayCache(ArrayCacheType.DOUBLE_VECTOR);
                resultTimeList = new ArrayCache(ArrayCacheType.LONG_VECTOR);

                if (indexPosition.length() != 0)
                    positionList.add(0, indexPosition);

                Gson gson = new Gson();

                String request = gson.toJson(positionList);

                Message msg = ClientRequestMessageFactory.createTransactionalPortfolioComputeRequest(
                        getTemplateRegistry(), metricType, request, params, getOutboundMsgSequenceNumber(),
                        System.currentTimeMillis());

                responseMsg = sendAndAwaitResponse(msg, USER_LAYER_TIMEOUT_SECONDS_ESTIMATE);

                isFirstBlock = false;
            } else {

                Message msg = ClientRequestMessageFactory.createTransactionalPortfolioComputeRequest(
                        getTemplateRegistry(), metricType, "#NEXT#", "", getOutboundMsgSequenceNumber(),
                        System.currentTimeMillis());

                responseMsg = sendAndAwaitResponse(msg, USER_LAYER_TIMEOUT_SECONDS_ESTIMATE);

            }

            TransmitDataRequest response = ServerResponseMessageParser.parseTransmitDataRequest(responseMsg);

            if (response.getMsgType().contains("OK")) {

                Gson gson = new Gson();
                Type mapType = new TypeToken<CalculationStatusMessage>() {
                }.getType();

                CalculationStatusMessage statusMessg = gson.fromJson(response.getMsgType(), mapType);

                dimensions = statusMessg.getDimension();

                float[] data = response.getDataFloat();
                long[] time = response.getTime();

                resultValueList.writeAsDouble(data);

                resultTimeList.write(time);

                percent = Double.valueOf(response.getMsgBody());
                progressBar.printCompletionStatus(percent);

                if (response.getMsgType().contains("STOP")) {

                    info = statusMessg.getResultInfo();

                    isRun = false;
                }
            } else {

                throw new Exception(response.getMsgBody());
            }
        }

        if (dimensions != null)
            resultValueList.setDimensions(dimensions);

        MethodResult result = new MethodResult();
        result.setData("value", resultValueList);
        result.setData("time", resultTimeList);
        result.setInfo(info);

        return result;
    }

    public MethodResult getAllSymbolsList() throws Exception {

        Message responseMsg;
        Message msg = ClientRequestMessageFactory.createTransactionalPortfolioComputeRequest(getTemplateRegistry(),
                "ALL_SYMBOLS", "", "", getOutboundMsgSequenceNumber(), System.currentTimeMillis());

        responseMsg = sendAndAwaitResponse(msg, USER_LAYER_TIMEOUT_SECONDS_ESTIMATE);

        TransmitDataRequest response = ServerResponseMessageParser.parseTransmitDataRequest(responseMsg);
        MethodResult result = new MethodResult();

        if (response.getMsgType().contains("OK")) {

            byte data[] = response.getDataFloatByte();

            data = Snappy.uncompress(data, 0, data.length);
            Map<String, String[]> map = (Map<String, String[]>) SerializationUtils.deserialize(data);

            ArrayCache id = new ArrayCache(map.get("id"));
            ArrayCache description = new ArrayCache(map.get("description"));
            ArrayCache exchange = new ArrayCache(map.get("exchange"));

            result.setData("id", id);
            result.setData("description", description);
            result.setData("exchange", exchange);

        } else {

            throw new Exception(response.getMsgBody());
        }

        return result;
    }

    public MethodResult getComputeTimeLeft() {

        for (int i = 0; i < 3; i++) {
            Message responseMsg;

            try {
                Message msg = ClientRequestMessageFactory.createTransactionalPortfolioComputeRequest(
                        getTemplateRegistry(), "TIME_LEFT", "", "", getOutboundMsgSequenceNumber(),
                        System.currentTimeMillis());

                responseMsg = sendAndAwaitResponse(msg, USER_LAYER_TIMEOUT_SECONDS_ESTIMATE);

                TransmitDataRequest response = ServerResponseMessageParser.parseTransmitDataRequest(responseMsg);
                MethodResult result = new MethodResult();

                if (response.getMsgType().contains("OK")) {

                    String data[] = response.getMsgBody().split("#");
                    HashMap<String, String> info = new HashMap<String, String>();
                    info.put("timeLeft", data[0]);
                    info.put("timeMax", data[1]);
                    result.setInfo(info);

                } else {

                    throw new Exception(response.getMsgBody());
                }

                return result;

            } catch (Exception e) {

                MethodResult result = processException(e);
                if (result == null)
                    continue;
                return result;
            }

        }
        return new MethodResult(MessageStrings.FAILED_SERVER_TIME_OUT);

    }

    private MethodResult processException(Exception e) {

        if (e instanceof ConnectFailedException) {

            MethodResult isRestarted = restart();
            if (isRestarted.hasError()) {
                resetProgressBar();
                return new MethodResult(isRestarted.getErrorMessage());
            }

            return null;
        }

        if (e.getMessage() == null || e.getMessage().contains("No data in cache")
                || e.getMessage().contains("null")) {

            MethodResult isRestarted = restart();
            if (isRestarted.hasError()) {
                resetProgressBar();
                return new MethodResult(isRestarted.getErrorMessage());
            }

            return null;

        }

        if (e.getMessage() == null) {
            Console.writeStackTrace(e);
            return new MethodResult(MessageStrings.ERROR_101);
        }

        resetProgressBar();
        return new MethodResult(e.getMessage());

    }

    private Message sendAndAwaitResponse(Message request, int timeoutSec) throws Exception {

        if (!isConnected)
            throw new ConnectFailedException();

        try {
            out.writeMessage(request);
        } catch (Exception e) {
            throw new ConnectFailedException();
        }

        ClientMessage clientMessage = clientMessageQueue.poll(timeoutSec, TimeUnit.SECONDS);
        clientMessageQueue.clear();

        if (clientMessage == null) {
            throw new ConnectFailedException();
        }
        if (clientMessage == null || clientMessage.isEmpty()) {

            throw new ConnectFailedException();
        } else if (clientMessage.isRejected()) {

            Reject reject = ServerResponseMessageParser.parseReject(clientMessage.getMessage());
            throw new Exception(reject.getText());
        }

        return clientMessage.getMessage();

    }

    private void sendHeartbeat(String testReqId) {
        Message logoutMsg = ClientRequestMessageFactory.createHeartbeat(templateRegistry,
                nextOutboundMsgSequenceNumber(), testReqId);
        out.writeMessage(logoutMsg);
    }

    private TemplateRegistry getTemplateRegistry() throws Exception {
        if (templateRegistry == null) {
            throw new ConnectFailedException();
        }
        return templateRegistry;
    }

    private FastMessageType getMessageType(Message msg) {
        String msgTypeCode = msg.getString("MessageType");
        FastMessageType fastMsgType = FastMessageType.getFastMessageType(msgTypeCode);
        return fastMsgType;
    }

    private class InboundMessageWorker implements Runnable {
        public void run() {
            try {
                logger.debug("Started inbound message logger thread");
                while (true) {

                    Message msg = in.readMessage();
                    if (msg == null) {
                        logger.debug("End of input stream. Exiting from inbound message worker thread");
                        break;
                    }

                    FastMessageType responseMessageType = getMessageType(msg);

                    serviceMessageQueue.offer(new ServiceMessage(System.currentTimeMillis()));

                    switch (responseMessageType) {
                    case TEST_REQUEST:
                        TestRequest testRequest = ServerResponseMessageParser.parseTestRequest(msg);
                        sendHeartbeat(testRequest.getTestReqID());
                        break;
                    case HEARTBEAT:
                        break;
                    case LOGON:
                        isLoggedOn = true;
                        clientMessageQueue.offer(new ClientMessage(msg));
                        break;
                    case LOGOUT:
                        isLoggedOn = false;
                        clientMessageQueue.offer(new ClientMessage(msg));
                        break;
                    case REJECT:
                        clientMessageQueue.offer(new ClientMessage(msg, true, false));
                        break;
                    case GET_REMAINING_TRAFFIC_RESPONSE:
                        clientMessageQueue.offer(new ClientMessage(msg));
                        break;
                    case NON_PARAM_METRIC_RESPONSE:
                        clientMessageQueue.offer(new ClientMessage(msg));
                        break;
                    case PORTFOLIO_ESTIMATION_RESPONSE:
                        clientMessageQueue.offer(new ClientMessage(msg));
                        break;
                    default:
                        clientMessageQueue.offer(new ClientMessage(msg));
                        break;
                    }

                    if (isMessageLoggingEnabled)
                        logger.info("Recieved message: " + msg.toString());
                }
            } catch (FastException e) {
                isLoggedOn = false;
                isConnected = false;
                logger.info("Connection was terminated");
            }
        }
    }

    private class HeartbeatMonitor implements Runnable {

        @Override
        public void run() {

            while (!Thread.interrupted()) {
                try {
                    ServiceMessage serviceMessage = serviceMessageQueue.poll(SERVICE_TIMEOUT_SEC, TimeUnit.SECONDS);

                    if (serviceMessage == null) {
                        clientMessageQueue.offer(new ClientMessage(null, false, true));
                    } else if (serviceMessage.isTerminated()) {
                        break;
                    } else {
                    }

                } catch (Exception e) {

                    break;
                }
            }
        }

    }

    public String getApiKey() {
        return apiKey;
    }

    public void setApiKey(String apiKey) {
        this.apiKey = apiKey;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getTemplatesFileName() {
        return templatesFileName;
    }

    public void setTemplatesFileName(String templatesFileName) {
        this.templatesFileName = templatesFileName;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public String getHost() {
        return host;
    }

    public void setMessageLoggingEnabled(boolean isMessageLoggingEnabled) {
        this.isMessageLoggingEnabled = isMessageLoggingEnabled;
    }

    public boolean isMessageLoggingEnabled() {
        return isMessageLoggingEnabled;
    }

    public boolean isLoggedOn() {
        return isLoggedOn;
    }

    public boolean isConnected() {
        return isConnected;
    }

    public int nextOutboundMsgSequenceNumber() {
        return outboundMsgSeqNum++;
    }

    public int getOutboundMsgSequenceNumber() {
        return outboundMsgSeqNum++;
    }

    public boolean isDebugModeEnabled() {
        return debugModeEnabled;
    }

    public void setDebugModeEnabled(boolean debugModeEnabled) {
        this.debugModeEnabled = debugModeEnabled;
    }

    public String getStatus() {
        return callStatus.toString();
    }

    @Override
    protected void finalize() throws Throwable {

        this.stop();

        super.finalize();
    }

}