/** * Copyright 2016 LinkedIn Corp. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ package com.github.ambry.protocol; import com.github.ambry.clustermap.ClusterMap; import com.github.ambry.commons.ServerErrorCode; import com.github.ambry.network.Send; import com.github.ambry.utils.Utils; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.channels.WritableByteChannel; import java.util.ArrayList; import java.util.List; /** * Response to GetRequest to fetch data */ public class GetResponse extends Response { private Send toSend = null; private InputStream stream = null; private final List<PartitionResponseInfo> partitionResponseInfoList; private int partitionResponseInfoSize; private static int Partition_Response_Info_List_Size = 4; static final short Get_Response_Version_V1 = 1; static final short Get_Response_Version_V2 = 2; // @todo change this to V2 once all cluster nodes understand V2. private static final short currentVersion = Get_Response_Version_V1; public GetResponse(int correlationId, String clientId, List<PartitionResponseInfo> partitionResponseInfoList, Send send, ServerErrorCode error) { super(RequestOrResponseType.GetResponse, currentVersion, correlationId, clientId, error); this.partitionResponseInfoList = partitionResponseInfoList; this.partitionResponseInfoSize = 0; for (PartitionResponseInfo partitionResponseInfo : partitionResponseInfoList) { this.partitionResponseInfoSize += partitionResponseInfo.sizeInBytes(); } this.toSend = send; } public GetResponse(int correlationId, String clientId, List<PartitionResponseInfo> partitionResponseInfoList, InputStream stream, ServerErrorCode error) { super(RequestOrResponseType.GetResponse, currentVersion, correlationId, clientId, error); this.partitionResponseInfoList = partitionResponseInfoList; this.partitionResponseInfoSize = 0; for (PartitionResponseInfo partitionResponseInfo : partitionResponseInfoList) { this.partitionResponseInfoSize += partitionResponseInfo.sizeInBytes(); } this.stream = stream; } public GetResponse(int correlationId, String clientId, ServerErrorCode error) { super(RequestOrResponseType.GetResponse, currentVersion, correlationId, clientId, error); this.partitionResponseInfoList = null; this.partitionResponseInfoSize = 0; } public InputStream getInputStream() { return stream; } public List<PartitionResponseInfo> getPartitionResponseInfoList() { return partitionResponseInfoList; } public static GetResponse readFrom(DataInputStream stream, ClusterMap map) throws IOException { short typeval = stream.readShort(); RequestOrResponseType type = RequestOrResponseType.values()[typeval]; if (type != RequestOrResponseType.GetResponse) { throw new IllegalArgumentException("The type of request response is not compatible"); } Short versionId = stream.readShort(); // ignore version for now int correlationId = stream.readInt(); String clientId = Utils.readIntString(stream); ServerErrorCode error = ServerErrorCode.values()[stream.readShort()]; if (error != ServerErrorCode.No_Error) { return new GetResponse(correlationId, clientId, error); } else { int partitionResponseInfoCount = stream.readInt(); ArrayList<PartitionResponseInfo> partitionResponseInfoList = new ArrayList<PartitionResponseInfo>(partitionResponseInfoCount); for (int i = 0; i < partitionResponseInfoCount; i++) { PartitionResponseInfo partitionResponseInfo = PartitionResponseInfo.readFrom(stream, map, versionId); partitionResponseInfoList.add(partitionResponseInfo); } return new GetResponse(correlationId, clientId, partitionResponseInfoList, stream, error); } } @Override public long writeTo(WritableByteChannel channel) throws IOException { long written = 0; if (bufferToSend == null) { bufferToSend = ByteBuffer.allocate( (int) super.sizeInBytes() + (Partition_Response_Info_List_Size + partitionResponseInfoSize)); writeHeader(); if (partitionResponseInfoList != null) { bufferToSend.putInt(partitionResponseInfoList.size()); for (PartitionResponseInfo partitionResponseInfo : partitionResponseInfoList) { partitionResponseInfo.writeTo(bufferToSend); } } bufferToSend.flip(); } if (bufferToSend.remaining() > 0) { written = channel.write(bufferToSend); } if (bufferToSend.remaining() == 0 && toSend != null && !toSend.isSendComplete()) { written += toSend.writeTo(channel); } return written; } @Override public boolean isSendComplete() { return (super.isSendComplete()) && (toSend == null || toSend.isSendComplete()); } @Override public long sizeInBytes() { return super.sizeInBytes() + (Partition_Response_Info_List_Size + partitionResponseInfoSize) + ((toSend == null) ? 0 : toSend.sizeInBytes()); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("GetResponse["); if (toSend != null) { sb.append("SizeToSend=").append(toSend.sizeInBytes()); } sb.append(" ServerErrorCode=").append(getError()); if (partitionResponseInfoList != null) { sb.append(" PartitionResponseInfoList=").append(partitionResponseInfoList); } sb.append("]"); return sb.toString(); } /** * @return the current version in which new GetResponse objects are created. */ static short getCurrentVersion() { return currentVersion; } }