/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.hdfs;
import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import org.apache.hadoop.hdfs.profiling.DFSWriteProfilingData.WritePacketClientProfile;
import org.apache.hadoop.hdfs.protocol.DataTransferProtocol;
import org.apache.hadoop.hdfs.server.datanode.DataNode;
/****************************************************************
* Packet for DFSOutputStream to use to construct the packet to send to data
* node.
****************************************************************/
abstract class DFSOutputStreamPacket {
protected final DFSOutputStream dfsOutputStream;
long seqno; // sequencenumber of buffer in block
long offsetInBlock; // offset in block
boolean lastPacketInBlock; // is this the last packet in block?
int dataLength;
int numChunks; // number of chunks currently in packet
int maxChunks; // max chunks in packet
WritePacketClientProfile profile;
static final long HEART_BEAT_SEQNO = -1L;
/**
* create a heartbeat packet
*/
protected DFSOutputStreamPacket(DFSOutputStream dfsOutputStream) {
this.dfsOutputStream = dfsOutputStream;
this.lastPacketInBlock = false;
this.numChunks = 0;
this.offsetInBlock = 0;
this.seqno = HEART_BEAT_SEQNO;
dataLength = 0;
maxChunks = 0;
}
// create a new packet
protected DFSOutputStreamPacket(DFSOutputStream dfsOutputStream,
int chunksPerPkt, long offsetInBlock, WritePacketClientProfile profile)
throws IOException {
this.dfsOutputStream = dfsOutputStream;
this.lastPacketInBlock = false;
this.numChunks = 0;
this.offsetInBlock = offsetInBlock;
this.seqno = this.dfsOutputStream.incAndGetCurrentSeqno();
this.profile = profile;
maxChunks = chunksPerPkt;
if (this.profile != null) {
this.profile.packetCreated();
}
}
void writeDataAndChecksum(byte[] dataInarray, int dataOff, int dataLen,
byte[] checksumInarray, int checksumOff, int checksumLen)
throws IOException {
writeData(dataInarray, dataOff, dataLen);
writeChecksum(checksumInarray, checksumOff, checksumLen);
}
abstract void writeData(byte[] inarray, int off, int len) throws IOException;
abstract void writeChecksum(byte[] inarray, int off, int len)
throws IOException;
abstract void cleanup();
/**
* Returns ByteBuffer that contains one full packet, including header.
*
* Packet format:
*
* +----------------------------+
* | size of payload |
* +----------------------------+
* | packetVersion* |
* +----------------------------+
* | offset of data in block |
* +----------------------------+
* | packet's seq ID |
* +----------------------------+
* | |
* | payload** |
* | |
* +----------------------------+
*
* * Only for data protocol version not ealier than
* PACKET_INCLUDE_VERSION_VERSION
* ** payload format is per packet version and will be described below.
*
* If packet size = 0, it indicates the end of the stream and there will be
* no packetVersion or data after it sent.
*
* == payload format ==
*
* packetVersion = PACKET_VERSION_CHECKSUM_FIRST:
* +----------------------------+
* | data size to be sent |
* +----------------------------+
* | checksum for chunk 1 |
* +----------------------------+
* | checksum for chunk 2 |
* +----------------------------+
* | ...... |
* +----------------------------+
* | |
* | data chunk 1 |
* | |
* +----------------------------+
* | |
* | data chunk 2 |
* | |
* +----------------------------+
* | |
* | ... |
* | |
* +----------------------------+
* | |
* | last data chunk |
* | (can be partial) |
* +----------------------------+
*
* packetVersion = PACKET_VERSION_CHECKSUM_INLINE:
* +----------------------------+
* | data size to be sent |
* +----------------------------+
* | |
* | data chunk 1 |
* | |
* +----------------------------+
* | checksum for chunk 1 |
* +----------------------------+
* | |
* | data chunk 2 |
* | |
* +----------------------------+
* | checksum for chunk 2 |
* +----------------------------+
* | |
* | ... |
* | |
* +----------------------------+
* | |
* | last data chunk |
* | (can be partial) |
* +----------------------------+
* | checksum for last chunk |
* +----------------------------+
*
* The way data and checksums are inlined is the same as inline checksum
* on disk files.
*
* @throws IOException
*/
abstract ByteBuffer getBuffer() throws IOException;
long getEndPosInCurrBlk() {
return offsetInBlock + dataLength;
}
/**
* Check if this packet is a heart beat packet
*
* @return true if the sequence number is HEART_BEAT_SEQNO
*/
boolean isHeartbeatPacket() {
return seqno == HEART_BEAT_SEQNO;
}
void eventAddToDataQueue() {
if (profile != null) {
profile.addToDataQueue();
}
}
void eventPopFromDataQueue() {
if (profile != null) {
profile.popFromDataQueue();
}
}
void eventAddToAckQueue() {
if (profile != null) {
profile.addToAckQueue();
}
}
void eventAckReceived() {
if (profile != null) {
profile.ackReceived();
}
}
}