/*
* Copyright (C) 2015 The Async HBase Authors. All rights reserved.
* This file is part of Async HBase.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the StumbleUpon nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.hbase.async;
import java.util.List;
import org.hbase.async.generated.ClientPB;
import org.hbase.async.generated.ClientPB.Result;
import org.hbase.async.generated.RPCPB;
import org.hbase.async.generated.CellPB.Cell;
import org.hbase.async.generated.ClientPB.MultiResponse;
import org.hbase.async.generated.ClientPB.RegionActionResult;
import org.hbase.async.generated.ClientPB.ResultOrException;
import org.hbase.async.generated.ClientPB.RegionActionResult.Builder;
import org.hbase.async.generated.HBasePB.NameBytesPair;
import org.hbase.async.generated.RPCPB.ResponseHeader;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.junit.Ignore;
import com.google.protobuf.ByteString;
import com.google.protobuf.CodedOutputStream;
import com.google.protobuf.GeneratedMessageLite;
@Ignore // ignore for test runners
public class PBufResponses {
/**
* Generate a full getRequest response with frame length and meta for decode()
* @param id The RPC ID to encode
* @param kvs The KVs to serialize
* @return A buffer to pass upstream
*/
public static ChannelBuffer getRequest(final int id, final List<KeyValue> kvs)
throws Exception {
final ClientPB.Result.Builder result = ClientPB.Result.newBuilder();
for (final KeyValue kv : kvs) {
result.addCell(kvToCell(kv));
}
final ClientPB.GetResponse response = ClientPB.GetResponse.newBuilder()
.setResult(result.build()).build();
final RPCPB.ResponseHeader header = RPCPB.ResponseHeader.newBuilder()
.setCallId(id)
//.setCellBlockMeta(meta)
.build();
return writeToBuffer(header, response);
}
/**
* Serializes a PBuf response with a frame length for upstream processing
* @param header The header to encode
* @param response The response to encode
* @return The buffer to pass upstream
*/
static ChannelBuffer writeToBuffer(final ResponseHeader header,
final GeneratedMessageLite response) throws Exception {
final int hlen = header.getSerializedSize();
final int vhlen = CodedOutputStream.computeRawVarint32Size(hlen);
final int pblen = response != null ? response.getSerializedSize() : 0;
final int vlen = CodedOutputStream.computeRawVarint32Size(pblen);
final byte[] buf = new byte[hlen + vhlen + vlen + pblen + 4];
final CodedOutputStream out = CodedOutputStream.newInstance(buf, 4,
hlen + vhlen + vlen + pblen);
out.writeMessageNoTag(header);
if (response != null) {
out.writeMessageNoTag(response);
}
Bytes.setInt(buf, buf.length - 4);
return ChannelBuffers.wrappedBuffer(buf);
}
/**
* Convert a key value pair to a cell for responses
* @param kv The KV to convert
* @return a cell
*/
static Cell kvToCell(final KeyValue kv) {
return Cell.newBuilder()
.setRow(Bytes.wrap(kv.key()))
.setFamily(Bytes.wrap(kv.family()))
.setQualifier(Bytes.wrap(kv.qualifier()))
.setValue(Bytes.wrap(kv.value()))
.setTimestamp(kv.timestamp())
.build();
}
/**
* Generate a multi-action response from the list of results. All results are
* considered part of a single region action.
* @param responses The results or exceptions to serialize
* @return A response PBuf object
*/
static MultiResponse generateMultiActionResponse(
final List<ResultOrException> responses) {
return MultiResponse.newBuilder().addRegionActionResult(
generateRegionActionResult(responses)).build();
}
/**
* Generate a multi-action response from a list of region action results
* @param responses The result sets to serialize
* @return A response PBuf object
*/
static MultiResponse generateMultiActionResponseFromRars(
final List<RegionActionResult> responses) {
MultiResponse.Builder builder = MultiResponse.newBuilder();
for (RegionActionResult rar : responses) {
builder.addRegionActionResult(rar);
}
return builder.build();
}
/**
* Generate a region action result from a list of result/exceptions
* @param responses The responses to encode
* @return A region action result
*/
static RegionActionResult generateRegionActionResult(
final List<ResultOrException> responses) {
final Builder rar = RegionActionResult.newBuilder();
for (final ResultOrException roe : responses) {
rar.addResultOrException(roe);
}
return rar.build();
}
/**
* Convert a key value to a result for multi-actions
* @param kv The column to encode
* @param i The multi-action array index to encode
* @return The result
*/
static ResultOrException kvToROE(final KeyValue kv, final int i) {
final Result result = Result.newBuilder().addCell(kvToCell(kv)).build();
return ResultOrException.newBuilder()
.setResult(result).setIndex(i).build();
}
/**
* Convert the list of columns to a result set for a multi-action batched RPC
* @param kvs The list of columns to encode
* @param i The index of the parent RPC in the multi-action batch
* @return The result
*/
static ResultOrException kvsToROE(final List<KeyValue> kvs, final int i) {
final Result.Builder result = Result.newBuilder();
for (final KeyValue kv : kvs) {
result.addCell(kvToCell(kv));
}
return ResultOrException.newBuilder()
.setResult(result).setIndex(i).build();
}
/**
* Generates an empty result for an RPC such as returned from a PUT or DELETE
* @param i The index of the parent RPC in the multi-action batch
* @return The result
*/
static ResultOrException generateEmptyResult(final int i) {
return ResultOrException.newBuilder().setIndex(i).build();
}
/**
* Return an exception for a multi-action batched RPC
* @param ex The exception to encode
* @param i The index of the parent RPC in the batch
* @return The exception
*/
static ResultOrException generateException(final Throwable ex, final int i) {
return ResultOrException.newBuilder()
.setException(buildException(ex)).setIndex(i).build();
}
/**
* Encodes a response WITHOUT the frame length or header. Useful for
* individual parsing calls
* @param response The response to serialize
* @return A channel buffer to parse
*/
static ChannelBuffer encodeResponse(final GeneratedMessageLite response)
throws Exception {
final int pblen = response.getSerializedSize();
final int vlen = CodedOutputStream.computeRawVarint32Size(pblen);
final byte[] buf = new byte[vlen + pblen];
final CodedOutputStream out = CodedOutputStream.newInstance(buf, 0,
vlen + pblen);
out.writeMessageNoTag(response);
return ChannelBuffers.wrappedBuffer(buf);
}
/**
* Shamelessly pulled from org.apache.hadoop.hbase.protobuf.ResponseConverter
* to encode an exception as a PBuf response
* @param t The exception to encode
* @return A NameBytesPair to store in a response
*/
static NameBytesPair buildException(final Throwable t) {
NameBytesPair.Builder parameterBuilder = NameBytesPair.newBuilder();
parameterBuilder.setName(t.getClass().getName());
parameterBuilder.setValue(
//ByteString.copyFromUtf8(StringUtils.stringifyException(t))); // need HDP
ByteString.copyFromUtf8(t.toString()));
return parameterBuilder.build();
}
/**
* Generates a single PBuf with an exception instead of a response. For use,
* e.g., in responding to a GetRequest with an NSRE
* @param id The RPC ID
* @param clazz The remote exception class name
* @return A buffer you can pass to the Region Client
*/
static ChannelBuffer generateException(final int id, final String clazz)
throws Exception {
final RPCPB.ExceptionResponse response =
RPCPB.ExceptionResponse.newBuilder()
.setExceptionClassName(clazz)
.setStackTrace("mock stack trace")
.build();
final RPCPB.ResponseHeader header = RPCPB.ResponseHeader.newBuilder()
.setCallId(id)
.setException(response)
//.setCellBlockMeta(meta)
.build();
return writeToBuffer(header, null);
}
}