/*
* Copyright 2014 NAVER Corp.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.navercorp.pinpoint.profiler.sender;
import com.navercorp.pinpoint.common.util.ArrayUtils;
import com.navercorp.pinpoint.profiler.util.ByteBufferUtils;
import com.navercorp.pinpoint.profiler.util.ObjectPool;
import com.navercorp.pinpoint.thrift.io.HeaderTBaseSerializer;
import com.navercorp.pinpoint.thrift.io.SpanStreamConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author Taejin Koo
*/
public class SpanStreamSendData {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final ByteBuffer[] components;
private final SpanStreamSendDataSerializer encoder;
private final List<HeaderTBaseSerializer> usedSerializerList;
private final ObjectPool<HeaderTBaseSerializer> serializerPool;
private final int maxBufferSize;
private final int maxGatheringComponentCount;
private final int maxAvailableBufferCapacity;
private int availableBufferCapacity;
private int chunkCount = 0;
private int componentsIndex = 0;
private SpanStreamSendDataMode mode;
private long firstAccessTime = -1;
public SpanStreamSendData(int maxPacketSize, int maxGatheringComponentCount, ObjectPool<HeaderTBaseSerializer> serializerPool) {
this.maxBufferSize = maxPacketSize;
this.maxGatheringComponentCount = maxGatheringComponentCount - 2;
this.maxAvailableBufferCapacity = maxPacketSize - SpanStreamConstants.START_PROTOCOL_BUFFER_SIZE - SpanStreamConstants.END_PROTOCOL_BUFFER_SIZE;
this.availableBufferCapacity = maxAvailableBufferCapacity;
if (this.maxAvailableBufferCapacity <= 0) {
throw new IllegalArgumentException("Illegal maxPacketSize(" + maxPacketSize + ")");
}
if (this.maxGatheringComponentCount <= 0) {
throw new IllegalArgumentException("Illegal maxGatheringComponentCount(" + maxGatheringComponentCount + ")");
}
this.components = new ByteBuffer[maxGatheringComponentCount];
this.encoder = new SpanStreamSendDataSerializer();
this.usedSerializerList = new ArrayList<HeaderTBaseSerializer>();
this.serializerPool = serializerPool;
this.mode = SpanStreamSendDataMode.WAIT_BUFFER;
}
public boolean addBuffer(byte[] buffer) {
return addBuffer(ByteBuffer.wrap(buffer), null);
}
public boolean addBuffer(byte[] buffer, HeaderTBaseSerializer headerTbaseSerializer) {
return addBuffer(ByteBuffer.wrap(buffer), headerTbaseSerializer);
}
public boolean addBuffer(ByteBuffer buffer) {
return addBuffer(buffer, null);
}
public boolean addBuffer(ByteBuffer buffer, HeaderTBaseSerializer headerTbaseSerializer) {
ByteBuffer[] bufferArray = new ByteBuffer[1];
bufferArray[0] = buffer;
return addBuffer(bufferArray, null);
}
public boolean addBuffer(ByteBuffer[] buffers) {
return addBuffer(buffers, null);
}
public boolean addBuffer(ByteBuffer[] buffers, HeaderTBaseSerializer headerTbaseSerializer) {
if (firstAccessTime == -1) {
firstAccessTime = System.currentTimeMillis();
}
if (!checkAddAvailable(buffers)) {
return false;
}
if (headerTbaseSerializer != null) {
usedSerializerList.add(headerTbaseSerializer);
}
chunkCount++;
ByteBuffer chunkFlagBuffer = ByteBufferUtils.createByteBuffer(SpanStreamConstants.DEFAULT_CHUNK_FLAG_BUFFER_SIZE);
components[componentsIndex++] = chunkFlagBuffer;
int usedBufferLength = 0;
for (ByteBuffer buffer : buffers) {
if (buffer.remaining() <= 0) {
continue;
}
components[componentsIndex++] = buffer;
usedBufferLength += buffer.remaining();
}
ByteBufferUtils.putShort(chunkFlagBuffer, (short) (usedBufferLength));
chunkFlagBuffer.flip();
this.availableBufferCapacity = this.availableBufferCapacity - usedBufferLength - SpanStreamConstants.DEFAULT_CHUNK_FLAG_BUFFER_SIZE;
return true;
}
private boolean checkAddAvailable(ByteBuffer[] bufferArray) {
if (ArrayUtils.isEmpty(bufferArray)) {
return false;
}
// check components count
// input buffer + chunk buffer
if (bufferArray.length + 1 > getAvailableGatheringComponentsCount()) {
return false;
}
int usedBufferLength = 0;
for (ByteBuffer buffer : bufferArray) {
usedBufferLength += buffer.remaining();
}
if (usedBufferLength == 0) {
return false;
}
// check buffer total size
return isAvailableBufferCapacity(usedBufferLength);
}
public boolean isAvailableBufferCapacity(int length) {
logger.debug("inputdata-length:{}, chunk-length:{}, available-length:{}", length, SpanStreamConstants.DEFAULT_CHUNK_FLAG_BUFFER_SIZE, availableBufferCapacity);
if (length + SpanStreamConstants.DEFAULT_CHUNK_FLAG_BUFFER_SIZE < availableBufferCapacity) {
return true;
} else {
return false;
}
}
public boolean isAvailableComponentsCount(int componentsCount) {
int availableGatheringComponentsCount = getAvailableGatheringComponentsCount();
logger.debug("inputcomponents-count:{}, availablecomponents-count:{}", componentsCount, availableGatheringComponentsCount);
if (componentsCount < availableGatheringComponentsCount) {
return true;
}
return false;
}
public int getMaxBufferCapacity() {
return maxBufferSize;
}
public int getAvailableBufferCapacity() {
return availableBufferCapacity;
}
public int getAvailableGatheringComponentsCount() {
return maxGatheringComponentCount - componentsIndex;
}
public ByteBuffer[] getSendBuffers() {
int writeIndex = 0;
ByteBuffer[] sendData = new ByteBuffer[2 + componentsIndex];
sendData[writeIndex++] = encoder.createStartBuffer((byte) chunkCount);
for (int i = 0; i < componentsIndex; i++) {
sendData[writeIndex++] = components[i];
}
sendData[writeIndex++] = encoder.createEndBuffer();
return sendData;
}
public void setWaitBufferAndFlushMode() {
setFlushMode(SpanStreamSendDataMode.WAIT_BUFFER_AND_FLUSH);
}
public void setFlushMode() {
setFlushMode(SpanStreamSendDataMode.FLUSH);
}
public void setFlushMode(SpanStreamSendDataMode mode) {
this.mode = mode;
}
public SpanStreamSendDataMode getFlushMode() {
return mode;
}
public void done() {
for (HeaderTBaseSerializer eachSerializer : usedSerializerList) {
serializerPool.returnObject(eachSerializer);
}
}
public long getFirstAccessTime() {
return firstAccessTime;
}
@Override
public String toString() {
return "SpanStreamSendData [components=" + Arrays.toString(components) + ", encoder=" + encoder + ", usedSerializerList=" + usedSerializerList
+ ", serializerPool=" + serializerPool + ", maxBufferSize=" + maxBufferSize + ", maxGatheringComponentCount=" + maxGatheringComponentCount
+ ", maxAvailableBufferCapacity=" + maxAvailableBufferCapacity + ", availableBufferCapacity=" + availableBufferCapacity + ", chunkCount="
+ chunkCount + ", componentsIndex=" + componentsIndex + ", mode=" + mode + "]";
}
}