/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library 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 Lesser General Public License for more
* details.
*/
package com.liferay.portal.kernel.nio.intraband;
import com.liferay.portal.kernel.io.BigEndianCodec;
import com.liferay.portal.kernel.nio.intraband.CompletionHandler.CompletionType;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.StringPool;
import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel;
import java.util.EnumSet;
/**
* Represents the communication unit of Intraband.
*
* <p>
* Encodes/decodes data to/from big-endian byte order data format:
* </p>
*
* <p>
* <table border="1">
*
* <tr>
* <td>Name</td><td>Type</td><td>Size(byte)</td><td>Offset</td>
* </tr>
* <tr>
* <td>Status Flag</td><td>byte</td><td>1</td><td>0</td>
* </tr>
* <tr>
* <td>Sequence ID</td><td>long</td><td>8</td><td>1</td>
* </tr>
* <tr>
* <td>Data Type</td><td>byte</td><td>1</td><td>9</td>
* </tr>
* <tr>
* <td>Data Size</td><td>int</td><td>4</td><td>10</td>
* </tr>
* <tr>
* <td>Data Chunk</td>
* <td>byte[]</td>
* <td>
* <pre>${Data Size}</pre>
* </td> <td>14</td> </tr>
*
* </table>
* </p>
*
* @author Shuyang Zhou
*/
public class Datagram {
public static Datagram createRequestDatagram(byte type, byte[] data) {
return createRequestDatagram(type, ByteBuffer.wrap(data));
}
public static Datagram createRequestDatagram(
byte type, ByteBuffer dataByteBuffer) {
Datagram datagram = new Datagram();
// Status flag
datagram._headerBufferArray[_INDEX_STATUS_FLAG] = _FLAG_REQUEST;
// Request datagram does not set the sequence ID
// Data type
datagram._headerBufferArray[_INDEX_DATA_TYPE] = type;
// Data size
BigEndianCodec.putInt(
datagram._headerBufferArray, _INDEX_DATA_SIZE,
dataByteBuffer.remaining());
// Data chunk
datagram._dataByteBuffer = dataByteBuffer;
return datagram;
}
public static Datagram createResponseDatagram(
Datagram requestDatagram, byte[] data) {
return createResponseDatagram(requestDatagram, ByteBuffer.wrap(data));
}
public static Datagram createResponseDatagram(
Datagram requestDatagram, ByteBuffer byteBuffer) {
Datagram datagram = new Datagram();
// Status flag
datagram._headerBufferArray[_INDEX_STATUS_FLAG] = _FLAG_RESPONSE;
// Sequence ID
BigEndianCodec.putLong(
datagram._headerBufferArray, _INDEX_SEQUENCE_ID,
requestDatagram.getSequenceId());
// Response datagram does not set the data type
// Data size
BigEndianCodec.putInt(
datagram._headerBufferArray, _INDEX_DATA_SIZE,
byteBuffer.remaining());
// Data chunk
datagram._dataByteBuffer = byteBuffer;
return datagram;
}
public ByteBuffer getDataByteBuffer() {
return _dataByteBuffer;
}
public byte getType() {
return _headerBufferArray[_INDEX_DATA_TYPE];
}
@Override
public String toString() {
StringBundler sb = new StringBundler(11);
sb.append("{dataChunk=");
ByteBuffer byteBuffer = _dataByteBuffer;
if (byteBuffer == null) {
sb.append(StringPool.NULL);
}
else {
sb.append(byteBuffer.toString());
}
sb.append(", dataSize=");
sb.append(BigEndianCodec.getInt(_headerBufferArray, _INDEX_DATA_SIZE));
sb.append(", dataType=");
sb.append(_headerBufferArray[_INDEX_DATA_TYPE]);
sb.append(", sequenceId=");
sb.append(
BigEndianCodec.getLong(_headerBufferArray, _INDEX_SEQUENCE_ID));
sb.append(", statusFlag=");
sb.append(_headerBufferArray[_INDEX_STATUS_FLAG]);
sb.append("}");
return sb.toString();
}
protected static Datagram createACKResponseDatagram(long sequenceId) {
Datagram datagram = new Datagram();
// Status flag
datagram._headerBufferArray[_INDEX_STATUS_FLAG] = _FLAG_ACK_RESPONSE;
// Sequence ID
BigEndianCodec.putLong(
datagram._headerBufferArray, _INDEX_SEQUENCE_ID, sequenceId);
// ACK response datagram does not set the data type
// Data size
BigEndianCodec.putInt(datagram._headerBufferArray, _INDEX_DATA_SIZE, 0);
// Data chunk
datagram._dataByteBuffer = _EMPTY_BUFFER;
return datagram;
}
protected static Datagram createReceiveDatagram() {
return new Datagram();
}
protected long getSequenceId() {
return BigEndianCodec.getLong(_headerBufferArray, _INDEX_SEQUENCE_ID);
}
protected boolean isAckRequest() {
byte statusFlag = _headerBufferArray[_INDEX_STATUS_FLAG];
if ((statusFlag & _FLAG_ACK_REQUEST) != 0) {
return true;
}
else {
return false;
}
}
protected boolean isAckResponse() {
byte statusFlag = _headerBufferArray[_INDEX_STATUS_FLAG];
if ((statusFlag & _FLAG_ACK_RESPONSE) != 0) {
return true;
}
else {
return false;
}
}
protected boolean isRequest() {
byte statusFlag = _headerBufferArray[_INDEX_STATUS_FLAG];
if ((statusFlag & _FLAG_REQUEST) != 0) {
return true;
}
else {
return false;
}
}
protected boolean isResponse() {
byte statusFlag = _headerBufferArray[_INDEX_STATUS_FLAG];
if ((statusFlag & _FLAG_RESPONSE) != 0) {
return true;
}
else {
return false;
}
}
protected boolean readFrom(ScatteringByteChannel scatteringByteChannel)
throws IOException {
if (_headerByteBuffer.hasRemaining()) {
if (scatteringByteChannel.read(_headerByteBuffer) == -1) {
throw new EOFException();
}
if (_headerByteBuffer.hasRemaining()) {
return false;
}
int dataSize = BigEndianCodec.getInt(
_headerBufferArray, _INDEX_DATA_SIZE);
if (dataSize == 0) {
_dataByteBuffer = _EMPTY_BUFFER;
return true;
}
_dataByteBuffer = ByteBuffer.allocate(dataSize);
}
if (scatteringByteChannel.read(_dataByteBuffer) == -1) {
throw new EOFException();
}
if (_dataByteBuffer.hasRemaining()) {
return false;
}
_dataByteBuffer.flip();
return true;
}
protected void setAckRequest(boolean ackRequest) {
byte statusFlag = _headerBufferArray[_INDEX_STATUS_FLAG];
if (ackRequest) {
statusFlag |= _FLAG_ACK_REQUEST;
}
else {
statusFlag &= ~_FLAG_ACK_REQUEST;
}
_headerBufferArray[_INDEX_STATUS_FLAG] = statusFlag;
}
protected void setSequenceId(long sequenceId) {
BigEndianCodec.putLong(
_headerBufferArray, _INDEX_SEQUENCE_ID, sequenceId);
}
protected boolean writeTo(GatheringByteChannel gatheringByteChannel)
throws IOException {
if (_headerByteBuffer.hasRemaining()) {
ByteBuffer[] byteBuffers = new ByteBuffer[2];
byteBuffers[0] = _headerByteBuffer;
byteBuffers[1] = _dataByteBuffer;
gatheringByteChannel.write(byteBuffers);
}
else {
gatheringByteChannel.write(_dataByteBuffer);
}
if (_dataByteBuffer.hasRemaining()) {
return false;
}
_dataByteBuffer = null;
return true;
}
protected Object attachment;
protected CompletionHandler<Object> completionHandler;
protected EnumSet<CompletionType> completionTypes;
protected long expireTime;
protected long timeout;
private Datagram() {
_headerByteBuffer = ByteBuffer.allocate(_HEADER_SIZE);
// Directly reference the interanl byte array for faster encoding and
// decoding
_headerBufferArray = _headerByteBuffer.array();
}
private static final ByteBuffer _EMPTY_BUFFER = ByteBuffer.allocate(0);
private static final byte _FLAG_ACK_REQUEST = 1;
private static final byte _FLAG_ACK_RESPONSE = 2;
private static final byte _FLAG_REQUEST = 4;
private static final byte _FLAG_RESPONSE = 8;
private static final int _HEADER_SIZE = 14;
private static final int _INDEX_DATA_SIZE = 10;
private static final int _INDEX_DATA_TYPE = 9;
private static final int _INDEX_SEQUENCE_ID = 1;
private static final int _INDEX_STATUS_FLAG = 0;
private ByteBuffer _dataByteBuffer;
private final byte[] _headerBufferArray;
private final ByteBuffer _headerByteBuffer;
}