Package org.voltdb

Source Code of org.voltdb.ClientResponseImpl

/* This file is part of VoltDB.
* Copyright (C) 2008-2014 VoltDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with VoltDB.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.voltdb;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;

import org.json_voltpatches.JSONException;
import org.json_voltpatches.JSONString;
import org.json_voltpatches.JSONStringer;
import org.voltdb.client.ClientResponse;
import org.voltdb.client.ClientUtils;
import org.voltdb.common.Constants;
import org.voltdb.utils.SerializationHelper;

/**
* Packages up the data to be sent back to the client as a stored
* procedure response in one FastSerialziable object.
*
*/
public class ClientResponseImpl implements ClientResponse, JSONString {
    private boolean setProperly = false;
    private byte status = 0;
    private String statusString = null;
    private byte encodedStatusString[];
    private byte appStatus = Byte.MIN_VALUE;
    private String appStatusString = null;
    private byte encodedAppStatusString[];
    private VoltTable[] results = new VoltTable[0];
    private Integer m_hash = null;

    private int clusterRoundTripTime = 0;
    private int clientRoundTripTime = 0;
    private long clientRoundTripTimeNanos = 0;

    // JSON KEYS FOR SERIALIZATION
    static final String JSON_STATUS_KEY = "status";
    static final String JSON_STATUSSTRING_KEY = "statusstring";
    static final String JSON_APPSTATUS_KEY = "appstatus";
    static final String JSON_APPSTATUSSTRING_KEY = "appstatusstring";
    static final String JSON_RESULTS_KEY = "results";
    static final String JSON_TYPE_KEY = "type";
    static final String JSON_EXCEPTION_KEY = "exception";

    // Error string returned when a replayed clog transaction is ignored or a replayed DR
    // transaction is a duplicate
    public static final String IGNORED_TRANSACTION = "Ignored replayed transaction";

    /** opaque data optionally provided by and returned to the client */
    private long clientHandle = -1;

    public ClientResponseImpl() {}

    /**
     * Used in the successful procedure invocation case.
     */
    public ClientResponseImpl(byte status, byte appStatus, String appStatusString, VoltTable[] results, String statusString) {
        this(status, appStatus, appStatusString, results, statusString, -1);
    }

    /**
     * Constructor used for tests and error responses.
     */
    public ClientResponseImpl(byte status, VoltTable[] results, String statusString) {
        this(status, ClientResponse.UNINITIALIZED_APP_STATUS_CODE, null, results, statusString, -1);
    }

    /**
     * Another constructor for test and error responses
     */
    public ClientResponseImpl(byte status, VoltTable[] results, String statusString, long handle) {
        this(status, ClientResponse.UNINITIALIZED_APP_STATUS_CODE, null, results, statusString, handle);
    }

    ClientResponseImpl(byte status, byte appStatus, String appStatusString, VoltTable[] results, String statusString, long handle) {
        this.appStatus = appStatus;
        this.appStatusString = appStatusString;
        setResults(status, results, statusString);
        clientHandle = handle;
    }

    private void setResults(byte status, VoltTable[] results, String statusString) {
        assert results != null;
        for (VoltTable result : results) {
            // null values are not permitted in results. If there is one, it will cause an
            // exception in writeExternal. This throws the exception sooner.
            assert result != null;
        }

        this.status = status;
        this.results = results;
        this.statusString = statusString;
        this.setProperly = true;
    }

    public void setHash(Integer hash) {
        m_hash = hash;
    }

    @Override
    public byte getStatus() {
        return status;
    }

    @Override
    public VoltTable[] getResults() {
        return results;
    }

    @Override
    public String getStatusString() {
        return statusString;
    }

    public void setClientHandle(long aHandle) {
        clientHandle = aHandle;
    }

    public long getClientHandle() {
        return clientHandle;
    }

    public Integer getHash() {
        return m_hash;
    }

    public void initFromBuffer(ByteBuffer buf) throws IOException {
        buf.get();//Skip version byte
        clientHandle = buf.getLong();
        byte presentFields = buf.get();
        status = buf.get();
        if ((presentFields & (1 << 5)) != 0) {
            statusString = SerializationHelper.getString(buf);
        } else {
            statusString = null;
        }
        appStatus = buf.get();
        if ((presentFields & (1 << 7)) != 0) {
            appStatusString = SerializationHelper.getString(buf);
        } else {
            appStatusString = null;
        }
        clusterRoundTripTime = buf.getInt();
        if ((presentFields & (1 << 6)) != 0) {
            throw new RuntimeException("Use of deprecated exception in Client Response serialization.");
        }
        if ((presentFields & (1 << 4)) != 0) {
            m_hash = buf.getInt();
        } else {
            m_hash = null;
        }
        int tableCount = buf.getShort();
        results = new VoltTable[tableCount];
        for (int i = 0; i < tableCount; i++) {
            int tableSize = buf.getInt();
            final int originalLimit = buf.limit();
            buf.limit(buf.position() + tableSize);
            final ByteBuffer slice = buf.slice();
            buf.position(buf.position() + tableSize);
            buf.limit(originalLimit);
            results[i] = new VoltTable(slice, false);
        }
        setProperly = true;
    }

    public int getSerializedSize() {
        int msgsize = 1 // version
            + 8 // clientHandle
            + 1 // present fields
            + 1 // status
            + 1 // app status
            + 4 // cluster roundtrip time
            + 2; // number of result tables

        if (appStatusString != null) {
            encodedAppStatusString = appStatusString.getBytes(Constants.UTF8ENCODING);
            msgsize += encodedAppStatusString.length + 4;
        }
        if (statusString != null) {
            encodedStatusString = statusString.getBytes(Constants.UTF8ENCODING);
            msgsize += encodedStatusString.length + 4;
        }
        if (m_hash != null) {
            msgsize += 4;
        }
        for (VoltTable vt : results) {
            msgsize += vt.getSerializedSize();
        }

        return msgsize;
    }

    /**
     * @return buf to allow call chaining.
     */
    public ByteBuffer flattenToBuffer(ByteBuffer buf) {
        assert setProperly;
        buf.put((byte)0); //version
        buf.putLong(clientHandle);
        byte presentFields = 0;
        if (appStatusString != null) {
            presentFields |= 1 << 7;
        }
        if (statusString != null) {
            presentFields |= 1 << 5;
        }
        if (m_hash != null) {
            presentFields |= 1 << 4;
        }
        buf.put(presentFields);
        buf.put(status);
        if (statusString != null) {
            buf.putInt(encodedStatusString.length);
            buf.put(encodedStatusString);
        }
        buf.put(appStatus);
        if (appStatusString != null) {
            buf.putInt(encodedAppStatusString.length);
            buf.put(encodedAppStatusString);
        }
        buf.putInt(clusterRoundTripTime);
        if (m_hash != null) {
            buf.putInt(m_hash.intValue());
        }
        buf.putShort((short)results.length);
        for (VoltTable vt : results)
        {
            vt.flattenToBuffer(buf);
        }
        return buf;
    }

    @Override
    public int getClusterRoundtrip() {
        return clusterRoundTripTime;
    }

    public void setClusterRoundtrip(int time) {
        clusterRoundTripTime = time;
    }

    @Override
    public int getClientRoundtrip() {
        return clientRoundTripTime;
    }

    @Override
    public long getClientRoundtripNanos() {
        return clientRoundTripTimeNanos;
    }

    public void setClientRoundtrip(long timeNanos) {
        clientRoundTripTimeNanos = timeNanos;
        clientRoundTripTime = (int)TimeUnit.NANOSECONDS.toMillis(timeNanos);
    }

    @Override
    public byte getAppStatus() {
        return appStatus;
    }

    @Override
    public String getAppStatusString() {
        return appStatusString;
    }

    public boolean isTransactionallySuccessful() {
        return isTransactionallySuccessful(status);
    }

    public static boolean isTransactionallySuccessful(byte status) {
        return (status == SUCCESS) || (status == OPERATIONAL_FAILURE);
    }

    @Override
    public String toJSONString() {
        JSONStringer js = new JSONStringer();
        try {
            js.object();

            js.key(JSON_STATUS_KEY);
            js.value(status);
            js.key(JSON_APPSTATUS_KEY);
            js.value(appStatus);
            js.key(JSON_STATUSSTRING_KEY);
            js.value(statusString);
            js.key(JSON_APPSTATUSSTRING_KEY);
            js.value(appStatusString);
            js.key(JSON_RESULTS_KEY);
            js.array();
            for (VoltTable o : results) {
                js.value(o);
            }
            js.endArray();

            js.endObject();
        }
        catch (JSONException e) {
            e.printStackTrace();
            throw new RuntimeException("Failed to serialized a parameter set to JSON.", e);
        }
        return js.toString();
    }

    /**
     * @return MD5 hash as int of the tables in the result. Only hashes first bits of big results.
     */
    public int getHashOfTableResults() {
        try {
            long cheesyChecksum = 0;
            for (int i = 0; i < results.length; ++i) {
                cheesyChecksum += ClientUtils.cheesyBufferCheckSum(results[i].m_buffer);
            }
            return (int)cheesyChecksum;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * Take the perfectly good results and convert them to a single long value
     * that stores the hash for determinism.
     *
     * This presumes the DR agent has no need for results. This probably saves
     * some small amount of bandwidth. The other proposed idea was using the status
     * string to hold the sql hash. Tossup... this seemed slightly more performant,
     * but also a bit icky.
     */
    public void convertResultsToHashForDeterminism() {
        int hash = m_hash == null ? 0 : m_hash;

        VoltTable t = new VoltTable(new VoltTable.ColumnInfo("", VoltType.INTEGER));
        t.addRow(hash);
        results = new VoltTable[] { t };
    }

    public void dropResultTable() {
        results = new VoltTable[] {};
    }
}
TOP

Related Classes of org.voltdb.ClientResponseImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.