All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.nio.Packet Maven / Gradle / Ivy

There is a newer version: 5.0-BETA-1
Show newest version
/*
 * Copyright (c) 2008-2015, 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.nio.serialization.Data;
import com.hazelcast.nio.serialization.DefaultData;

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.
 */
public final class Packet implements SocketWritable, SocketReadable {

    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 Data data;
    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(Data data) {
        this(data, -1);
    }

    public Packet(Data data, int partitionId) {
        this.data = data;
        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); } @Override public boolean writeTo(ByteBuffer destination) { if (!writeVersion(destination)) { return false; } if (!writeHeader(destination)) { return false; } if (!writePartition(destination)) { return false; } if (!writeSize(destination)) { return false; } if (!writeValue(destination)) { return false; } setPersistStatus(PERSIST_COMPLETED); return true; } @Override public boolean readFrom(ByteBuffer source) { if (!readVersion(source)) { return false; } if (!readHeader(source)) { return false; } if (!readPartition(source)) { return false; } if (!readSize(source)) { return false; } if (!readValue(source)) { return false; } setPersistStatus(PERSIST_COMPLETED); return true; } // ========================= version ================================================= private boolean readVersion(ByteBuffer source) { if (!isPersistStatusSet(PERSIST_VERSION)) { if (!source.hasRemaining()) { return false; } byte version = source.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 destination) { if (!isPersistStatusSet(PERSIST_VERSION)) { if (!destination.hasRemaining()) { return false; } destination.put(VERSION); setPersistStatus(PERSIST_VERSION); } return true; } // ========================= header ================================================= private boolean readHeader(ByteBuffer source) { if (!isPersistStatusSet(PERSIST_HEADER)) { if (source.remaining() < 2) { return false; } header = source.getShort(); setPersistStatus(PERSIST_HEADER); } return true; } private boolean writeHeader(ByteBuffer destination) { if (!isPersistStatusSet(PERSIST_HEADER)) { if (destination.remaining() < Bits.SHORT_SIZE_IN_BYTES) { return false; } destination.putShort(header); setPersistStatus(PERSIST_HEADER); } return true; } // ========================= partition ================================================= private boolean readPartition(ByteBuffer source) { if (!isPersistStatusSet(PERSIST_PARTITION)) { if (source.remaining() < 4) { return false; } partitionId = source.getInt(); setPersistStatus(PERSIST_PARTITION); } return true; } private boolean writePartition(ByteBuffer destination) { if (!isPersistStatusSet(PERSIST_PARTITION)) { if (destination.remaining() < Bits.INT_SIZE_IN_BYTES) { return false; } destination.putInt(partitionId); setPersistStatus(PERSIST_PARTITION); } return true; } // ========================= size ================================================= private boolean readSize(ByteBuffer source) { if (!isPersistStatusSet(PERSIST_SIZE)) { if (source.remaining() < INT_SIZE_IN_BYTES) { return false; } size = source.getInt(); setPersistStatus(PERSIST_SIZE); } return true; } private boolean writeSize(ByteBuffer destination) { if (!isPersistStatusSet(PERSIST_SIZE)) { if (destination.remaining() < INT_SIZE_IN_BYTES) { return false; } size = data.totalSize(); destination.putInt(size); setPersistStatus(PERSIST_SIZE); } return true; } // ========================= value ================================================= private boolean writeValue(ByteBuffer destination) { if (!isPersistStatusSet(PERSIST_VALUE)) { if (size > 0) { // the number of bytes that can be written to the bb. int bytesWritable = destination.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 = data.toByteArray(); destination.put(byteArray, valueOffset, bytesWrite); valueOffset += bytesWrite; if (!done) { return false; } } setPersistStatus(PERSIST_VALUE); } return true; } private boolean readValue(ByteBuffer source) { if (!isPersistStatusSet(PERSIST_VALUE)) { byte[] bytes; if (data == null) { bytes = new byte[size]; data = new DefaultData(bytes); } else { bytes = data.toByteArray(); } if (size > 0) { int bytesReadable = source.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. source.get(bytes, valueOffset, bytesRead); valueOffset += bytesRead; 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 size() { // 11 = byte(version) + short(header) + int(partitionId) + int(data size) return (data != null ? data.totalSize() : 0) + 11; } public Data getData() { return data; } public void setData(Data data) { this.data = data; } public boolean done() { return isPersistStatusSet(PERSIST_COMPLETED); } public void reset() { data = null; persistStatus = 0; } private void setPersistStatus(short persistStatus) { this.persistStatus = persistStatus; } private boolean isPersistStatusSet(short status) { return this.persistStatus >= status; } @Override public String toString() { final StringBuilder sb = new StringBuilder("Packet{"); sb.append("header=").append(header); sb.append(", isResponse=").append(isHeaderSet(Packet.HEADER_RESPONSE)); sb.append(", isOperation=").append(isHeaderSet(Packet.HEADER_OP)); sb.append(", isEvent=").append(isHeaderSet(Packet.HEADER_EVENT)); sb.append(", partitionId=").append(partitionId); sb.append(", conn=").append(conn); sb.append('}'); return sb.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy