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: 3.9.3
Show newest version
/*
 * 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 com.hazelcast.spi.annotation.PrivateApi;

import java.nio.ByteBuffer;

import static com.hazelcast.nio.Bits.BYTE_SIZE_IN_BYTES;
import static com.hazelcast.nio.Bits.INT_SIZE_IN_BYTES;
import static com.hazelcast.nio.Bits.SHORT_SIZE_IN_BYTES;

/**
 * A Packet is a piece of data send over the line. The Packet is used for member 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.
 */
@PrivateApi
public final class Packet extends HeapData implements OutboundFrame {

    public static final byte VERSION = 4;

    public static final int FLAG_OP = 1 << 0;
    public static final int FLAG_RESPONSE = 1 << 1;
    public static final int FLAG_EVENT = 1 << 2;
    public static final int FLAG_URGENT = 1 << 4;
    public static final int FLAG_BIND = 1 << 5;

    /**
     * A flag to indicate this is a special control packet for the operation system like invocation-heartbeats.
     */
    public static final int FLAG_OP_CONTROL = 1 << 6;

    private static final int HEADER_SIZE = BYTE_SIZE_IN_BYTES + SHORT_SIZE_IN_BYTES + INT_SIZE_IN_BYTES + INT_SIZE_IN_BYTES;

    private short flags;
    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 boolean headerComplete;

    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; } /** * Sets a particular flag. The other flags will not be touched. * * @param flag the flag to set * @return this (for fluent interface) */ public Packet setFlag(int flag) { flags = (short) (flags | flag); return this; } /** * Sets all flags at once. The old flags will be completely overwritten by the new flags. * * The reason this method accepts an int instead of a short is that Java immediately converts to ints, * so you would have to do bit shifting logic to down cast to a short all the time.* * * @param flags the flags. * @return this (for fluent interface) */ public Packet setAllFlags(int flags) { this.flags = (short) flags; return this; } /** * Checks if a flag is set. * * @param flag the flag to check * @return true if the flag is set, false otherwise. */ public boolean isFlagSet(int flag) { return (flags & flag) != 0; } /** * Returns the flags of the Packet. The flags is used to figure out what the content is of this Packet before * the actual payload needs to be processed. * * @return the flags. */ public short getFlags() { return flags; } /** * 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; } public void reset() { headerComplete = false; } @Override public boolean isUrgent() { return isFlagSet(FLAG_URGENT); } public boolean writeTo(ByteBuffer dst) { if (!headerComplete) { if (dst.remaining() < HEADER_SIZE) { return false; } dst.put(VERSION); dst.putShort(flags); dst.putInt(partitionId); size = totalSize(); dst.putInt(size); headerComplete = true; } return writeValue(dst); } public boolean readFrom(ByteBuffer src) { if (!headerComplete) { if (src.remaining() < HEADER_SIZE) { return false; } byte version = src.get(); if (VERSION != version) { throw new IllegalArgumentException("Packet versions are not matching! Expected -> " + VERSION + ", Incoming -> " + version); } flags = src.getShort(); partitionId = src.getInt(); size = src.getInt(); headerComplete = true; } return readValue(src); } // ========================= value ================================================= private boolean readValue(ByteBuffer src) { 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; } } return true; } private boolean writeValue(ByteBuffer dst) { 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; } } return true; } /** * Returns an estimation of the packet, including its payload, in bytes. * * @return the size of the packet. */ public int packetSize() { return (payload != null ? totalSize() : 0) + HEADER_SIZE; } @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 (flags != packet.flags) { return false; } return partitionId == packet.partitionId; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (int) flags; result = 31 * result + partitionId; return result; } @Override public String toString() { return "Packet{" + "flags=" + flags + ", isResponse=" + isFlagSet(Packet.FLAG_RESPONSE) + ", isOperation=" + isFlagSet(Packet.FLAG_OP) + ", isEvent=" + isFlagSet(Packet.FLAG_EVENT) + ", partitionId=" + partitionId + ", conn=" + conn + '}'; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy