
com.hazelcast.nio.Packet Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2016, Hazelcast, Inc. 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hazelcast.nio;
import com.hazelcast.internal.serialization.impl.HeapData;
import java.nio.ByteBuffer;
import static com.hazelcast.nio.Bits.INT_SIZE_IN_BYTES;
/**
* A Packet is a piece of data send over the line. The Packet is used for member to member communication and old-client to
* member communication.
*
* The Packet extends HeapData instead of wrapping it. From a design point of view this is often not the preferred solution (
* prefer composition over inheritance), but in this case that would mean more object litter.
*
* Since the Packet isn't used throughout the system, this design choice is visible locally.
*/
public final class Packet extends HeapData implements OutboundFrame {
public static final byte VERSION = 4;
public static final int HEADER_OP = 0;
public static final int HEADER_RESPONSE = 1;
public static final int HEADER_EVENT = 2;
public static final int HEADER_WAN_REPLICATION = 3;
public static final int HEADER_URGENT = 4;
public static final int HEADER_BIND = 5;
// The value of these constants is important. The order needs to match the order in the read/write process
private static final short PERSIST_VERSION = 1;
private static final short PERSIST_HEADER = 2;
private static final short PERSIST_PARTITION = 3;
private static final short PERSIST_SIZE = 4;
private static final short PERSIST_VALUE = 5;
private static final short PERSIST_COMPLETED = Short.MAX_VALUE;
private short header;
private int partitionId;
private transient Connection conn;
// These 2 fields are only used during read/write. Otherwise they have no meaning.
private int valueOffset;
private int size;
// Stores the current 'phase' of read/write. This is needed so that repeated calls can be made to read/write.
private short persistStatus;
public Packet() {
}
public Packet(byte[] payload) {
this(payload, -1);
}
public Packet(byte[] payload, int partitionId) {
super(payload);
this.partitionId = partitionId;
}
/**
* Gets the Connection this Packet was send with.
*
* @return the Connection. Could be null.
*/
public Connection getConn() {
return conn;
}
/**
* Sets the Connection this Packet is send with.
*
* This is done on the reading side of the Packet to make it possible to retrieve information about
* the sender of the Packet.
*
* @param conn the connection.
*/
public void setConn(Connection conn) {
this.conn = conn;
}
public void setHeader(int bit) {
header |= 1 << bit;
}
public boolean isHeaderSet(int bit) {
return (header & 1 << bit) != 0;
}
/**
* Returns the header of the Packet. The header is used to figure out what the content is of this Packet before
* the actual payload needs to be processed.
*
* @return the header.
*/
public short getHeader() {
return header;
}
/**
* Returns the partition id of this packet. If this packet is not for a particular partition, -1 is returned.
*
* @return the partition id.
*/
public int getPartitionId() {
return partitionId;
}
@Override
public boolean isUrgent() {
return isHeaderSet(HEADER_URGENT);
}
public boolean writeTo(ByteBuffer dst) {
if (!writeVersion(dst)) {
return false;
}
if (!writeHeader(dst)) {
return false;
}
if (!writePartition(dst)) {
return false;
}
if (!writeSize(dst)) {
return false;
}
if (!writeValue(dst)) {
return false;
}
setPersistStatus(PERSIST_COMPLETED);
return true;
}
public boolean readFrom(ByteBuffer src) {
if (!readVersion(src)) {
return false;
}
if (!readHeader(src)) {
return false;
}
if (!readPartition(src)) {
return false;
}
if (!readSize(src)) {
return false;
}
if (!readValue(src)) {
return false;
}
setPersistStatus(PERSIST_COMPLETED);
return true;
}
// ========================= version =================================================
private boolean readVersion(ByteBuffer src) {
if (!isPersistStatusSet(PERSIST_VERSION)) {
if (!src.hasRemaining()) {
return false;
}
byte version = src.get();
setPersistStatus(PERSIST_VERSION);
if (VERSION != version) {
throw new IllegalArgumentException("Packet versions are not matching! Expected -> "
+ VERSION + ", Incoming -> " + version);
}
}
return true;
}
private boolean writeVersion(ByteBuffer dst) {
if (!isPersistStatusSet(PERSIST_VERSION)) {
if (!dst.hasRemaining()) {
return false;
}
dst.put(VERSION);
setPersistStatus(PERSIST_VERSION);
}
return true;
}
// ========================= header =================================================
private boolean readHeader(ByteBuffer src) {
if (!isPersistStatusSet(PERSIST_HEADER)) {
if (src.remaining() < 2) {
return false;
}
header = src.getShort();
setPersistStatus(PERSIST_HEADER);
}
return true;
}
private boolean writeHeader(ByteBuffer dst) {
if (!isPersistStatusSet(PERSIST_HEADER)) {
if (dst.remaining() < Bits.SHORT_SIZE_IN_BYTES) {
return false;
}
dst.putShort(header);
setPersistStatus(PERSIST_HEADER);
}
return true;
}
// ========================= partition =================================================
private boolean readPartition(ByteBuffer src) {
if (!isPersistStatusSet(PERSIST_PARTITION)) {
if (src.remaining() < 4) {
return false;
}
partitionId = src.getInt();
setPersistStatus(PERSIST_PARTITION);
}
return true;
}
private boolean writePartition(ByteBuffer dst) {
if (!isPersistStatusSet(PERSIST_PARTITION)) {
if (dst.remaining() < Bits.INT_SIZE_IN_BYTES) {
return false;
}
dst.putInt(partitionId);
setPersistStatus(PERSIST_PARTITION);
}
return true;
}
// ========================= size =================================================
private boolean readSize(ByteBuffer src) {
if (!isPersistStatusSet(PERSIST_SIZE)) {
if (src.remaining() < INT_SIZE_IN_BYTES) {
return false;
}
size = src.getInt();
setPersistStatus(PERSIST_SIZE);
}
return true;
}
private boolean writeSize(ByteBuffer dst) {
if (!isPersistStatusSet(PERSIST_SIZE)) {
if (dst.remaining() < INT_SIZE_IN_BYTES) {
return false;
}
size = totalSize();
dst.putInt(size);
setPersistStatus(PERSIST_SIZE);
}
return true;
}
// ========================= value =================================================
private boolean readValue(ByteBuffer src) {
if (!isPersistStatusSet(PERSIST_VALUE)) {
if (payload == null) {
payload = new byte[size];
}
if (size > 0) {
int bytesReadable = src.remaining();
int bytesNeeded = size - valueOffset;
boolean done;
int bytesRead;
if (bytesReadable >= bytesNeeded) {
bytesRead = bytesNeeded;
done = true;
} else {
bytesRead = bytesReadable;
done = false;
}
// read the data from the byte-buffer into the bytes-array.
src.get(payload, valueOffset, bytesRead);
valueOffset += bytesRead;
if (!done) {
return false;
}
}
setPersistStatus(PERSIST_VALUE);
}
return true;
}
private boolean writeValue(ByteBuffer dst) {
if (!isPersistStatusSet(PERSIST_VALUE)) {
if (size > 0) {
// the number of bytes that can be written to the bb.
int bytesWritable = dst.remaining();
// the number of bytes that need to be written.
int bytesNeeded = size - valueOffset;
int bytesWrite;
boolean done;
if (bytesWritable >= bytesNeeded) {
// All bytes for the value are available.
bytesWrite = bytesNeeded;
done = true;
} else {
// Not all bytes for the value are available. So lets write as much as is available.
bytesWrite = bytesWritable;
done = false;
}
byte[] byteArray = toByteArray();
dst.put(byteArray, valueOffset, bytesWrite);
valueOffset += bytesWrite;
if (!done) {
return false;
}
}
setPersistStatus(PERSIST_VALUE);
}
return true;
}
/**
* Returns an estimation of the packet, including its payload, in bytes.
*
* @return the size of the packet.
*/
public int packetSize() {
// 11 = byte(version) + short(header) + int(partitionId) + int(data size)
return (payload != null ? totalSize() : 0) + 11;
}
public boolean done() {
return isPersistStatusSet(PERSIST_COMPLETED);
}
public void reset() {
payload = null;
persistStatus = 0;
}
private void setPersistStatus(short persistStatus) {
this.persistStatus = persistStatus;
}
private boolean isPersistStatusSet(short status) {
return this.persistStatus >= status;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Packet)) {
return false;
}
Packet packet = (Packet) o;
if (!super.equals(packet)) {
return false;
}
if (header != packet.header) {
return false;
}
return partitionId == packet.partitionId;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (int) header;
result = 31 * result + partitionId;
return result;
}
@Override
public String toString() {
return "Packet{"
+ "header=" + header
+ ", isResponse=" + isHeaderSet(Packet.HEADER_RESPONSE)
+ ", isOperation=" + isHeaderSet(Packet.HEADER_OP)
+ ", isEvent=" + isHeaderSet(Packet.HEADER_EVENT)
+ ", partitionId=" + partitionId
+ ", conn=" + conn
+ '}';
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy