package lsr.common; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.Serializable; import java.nio.ByteBuffer; import java.util.Arrays; /** * Represents the request of user which will be inserted into state machine * after deciding it. After executing this request, <code>Reply</code> message * is generated. * * @see Reply */ public final class ClientRequest implements Serializable, RequestType { /* * The Request class should be final. The custome deserialization does not * respect class hierarchy, so any class derived from request would be * deserialized as the base Request class, which could cause bugs if we rely * on type information in the code. */ private static final long serialVersionUID = 1L; /** Represents the NOP request */ public static final ClientRequest NOP = new ClientRequest(RequestId.NOP, new byte[0]); private final RequestId requestId; private final byte[] value; /** * Creates new <code>Request</code>. * * @param requestId - id of this request. Must not be null. * @param value - the value of request. Must not be null (but may be empty). */ public ClientRequest(RequestId requestId, byte[] value) { assert requestId != null : "Request ID cannot be null"; assert value != null : "Value cannot be null"; this.requestId = requestId; this.value = value; } /** * Reads a request from the given <code>ByteBuffer</code> and advances the * position on the buffer. * * @param buffer - the byte buffer with serialized request * @return deserialized request from input byte buffer */ public static ClientRequest create(ByteBuffer buffer) { long clientId = buffer.getLong(); int sequenceId = buffer.getInt(); RequestId requestId = new RequestId(clientId, sequenceId); byte[] value = new byte[buffer.getInt()]; buffer.get(value); return new ClientRequest(requestId, value); } /** For use of ForwardedRequest class */ public static ClientRequest create(DataInputStream input) throws IOException { long clientId = input.readLong(); int sequenceId = input.readInt(); RequestId requestId = new RequestId(clientId, sequenceId); byte[] value = new byte[input.readInt()]; input.readFully(value); return new ClientRequest(requestId, value); } /** * Returns the id of this request. * * @return id of request */ public RequestId getRequestId() { return requestId; } /** * Returns the value held by this request. * * @return the value of this request */ public byte[] getValue() { return value; } /** * The size of the request after serialization in bytes. * * @return the size of the request in bytes */ public int byteSize() { return 8 + 4 + 4 + value.length; } /** Used to determine how many bytes must be read as header */ public static final int HEADERS_SIZE = 8 + 4 + 4; /** After how many bytes the size of value is stored */ public static final int HEADER_VALUE_SIZE_OFFSET = 8 + 4; /** * Writes a message to specified byte buffer. The number of elements * remaining in specified buffer should be greater or equal than * <code>byteSize()</code>. * * @param bb - the byte buffer to write message to */ public void writeTo(ByteBuffer bb) { bb.putLong(requestId.getClientId()); bb.putInt(requestId.getSeqNumber()); bb.putInt(value.length); bb.put(value); } public void writeTo(DataOutputStream dos) throws IOException { dos.writeLong(requestId.getClientId()); dos.writeInt(requestId.getSeqNumber()); dos.writeInt(value.length); dos.write(value); } /** * Creates a byte array with the binary representation of the request. * * @return */ public byte[] toByteArray() { ByteBuffer byteBuffer = ByteBuffer.allocate(byteSize()); writeTo(byteBuffer); return byteBuffer.array(); } public boolean equals(Object obj) { if (!(obj == this)) { return true; } if (obj == null || obj.getClass() != this.getClass()) { return false; } ClientRequest request = (ClientRequest) obj; if (requestId.equals(request.requestId)) { assert Arrays.equals(value, request.value) : "Critical: identical RequestID, different value"; return true; } return false; } @Override public int hashCode() { return requestId.hashCode(); } public String toString() { return "id=" + requestId; } public boolean isNop() { return requestId.isNop(); } }