package protocolP2P; import protocolP2P.RequestResponseCode; import protocolP2P.FilePart; import protocolP2P.FileList; import protocolP2P.LoadRequest; import protocolP2P.HashRequest; import protocolP2P.HashResponse; import protocolP2P.DiscoverRequest; import protocolP2P.DiscoverResponse; import localException.ProtocolError; import localException.InternalError; import localException.TransmissionError; import localException.SizeError; import tools.BytesArrayTools; /** Representation of payload. If payload has a size, use subclasses instead. * @author Louis Royer * @author Flavien Haas * @author JS Auge * @version 1.0 */ public class Payload { protected RequestResponseCode requestResponseCode; protected final static int PAYLOAD_SIZE_POSITION = 4; protected final static int PAYLOAD_START_POSITION = 8; /** Consructor used to create Payload with a payload size of zero using a RRCode. * @param requestResponseCode Request/Response code associated with the payload * @throws InternalError */ public Payload(RequestResponseCode requestResponseCode) throws InternalError { /* asserts to help debugging */ assert requestResponseCode != RequestResponseCode.LIST_RESPONSE || (this instanceof FileList) : "LIST_RESPONSE must use FilePart class"; assert requestResponseCode != RequestResponseCode.LOAD_RESPONSE || (this instanceof FilePart) : "LOAD_RESPONSE must use FileList class"; assert requestResponseCode != RequestResponseCode.LOAD_REQUEST || (this instanceof LoadRequest) : "LOAD_REQUEST must use LoadRequest class"; assert requestResponseCode != RequestResponseCode.HASH_REQUEST || (this instanceof HashRequest) : "HASH_REQUEST must use HashRequest class"; assert requestResponseCode != RequestResponseCode.HASH_RESPONSE || (this instanceof HashResponse) : "HASH_RESPONSE must use HashResponse class"; assert requestResponseCode != RequestResponseCode.DISCOVER_REQUEST || (this instanceof DiscoverRequest) : "DISCOVER_REQUEST must use DiscoverRequest class"; assert requestResponseCode != RequestResponseCode.DISCOVER_RESPONSE || (this instanceof DiscoverResponse) : "DISCOVER_RESPONSE must use DiscoverResponse class"; this.requestResponseCode = requestResponseCode; checkRequestResponseCode(); // this can throw InternalError } /** Constructor used to create a Payload (when no more specific subclasses exists) using packet as parameter. * If payload size is not empty, using subclass is required. * @param packet the full packet received * @throws ProtocolError * @throws InternalError * @throws TransmissionError * @throws SizeError */ protected Payload(byte[] packet) throws SizeError, ProtocolError, InternalError, TransmissionError { /* asserts to help debugging */ assert getPayloadSize(packet) + PAYLOAD_START_POSITION <= packet.length : "Payload is truncated"; if (packet.length < getPayloadSize(packet) + PAYLOAD_START_POSITION) { throw new TransmissionError(); } assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LIST_RESPONSE || (this instanceof FileList) : "LIST_RESPONSE must use FilePart class"; assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LOAD_RESPONSE || (this instanceof FilePart) : "LOAD_RESPONSE must use FileList class"; assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LOAD_REQUEST || (this instanceof LoadRequest) : "LOAD_REQUEST must use LoadRequest class"; assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.HASH_REQUEST || (this instanceof HashRequest) : "HASH_REQUEST must use HashRequest class"; assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.HASH_RESPONSE || (this instanceof HashResponse) : "HASH_RESPONSE must use HashResponse class"; assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.DISCOVER_REQUEST || (this instanceof DiscoverRequest) : "DISCOVER_REQUEST must use DiscoverRequest class"; assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.DISCOVER_RESPONSE || (this instanceof DiscoverResponse) : "DISCOVER_RESPONSE must use DiscoverResponse class"; requestResponseCode = RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]); checkRequestResponseCode(); // this can throw InternalError } /** Used to check RRCode used is compatible with this class use, or if a more specific subclass is required. * @throws InternalError */ private void checkRequestResponseCode() throws InternalError { /* Incorrect use cases (use subclasses instead) */ if ((requestResponseCode == RequestResponseCode.LIST_RESPONSE && !(this instanceof FileList)) || (requestResponseCode == RequestResponseCode.LOAD_RESPONSE && !(this instanceof FilePart)) || (requestResponseCode == RequestResponseCode.LOAD_REQUEST && !(this instanceof LoadRequest)) || (requestResponseCode == RequestResponseCode.HASH_REQUEST && !(this instanceof HashRequest)) || (requestResponseCode == RequestResponseCode.HASH_RESPONSE && !(this instanceof HashResponse)) || (requestResponseCode == RequestResponseCode.DISCOVER_REQUEST && !(this instanceof DiscoverRequest)) || (requestResponseCode == RequestResponseCode.DISCOVER_RESPONSE && !(this instanceof DiscoverResponse)) ) { throw new InternalError(); } } /** Returns a byte[] containing Packet with padding. * This Packet is still incomplete and should not be send directly. * ProtocolP2PPacket will use this method to generate the complete Packet. * @return Packet with padding * @throws InternalError */ protected byte[] toPacket() throws InternalError { // InternalError is impossible in this method on Payload class but still on subclasses byte [] packet = new byte[8]; // java initialize all to zero // size is zero (and this is the default) // set request/response code packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue; // bits 16-31 are reserved for future use // payload size is 0 (this is what java have initialized Packet) return packet; } /** Set payload’s size in a Packet. * @param size integer representing payload size * @param packet Packet to be completed * @throws InternalError */ protected static void setPayloadSize(int size, byte[] packet) throws InternalError { /* assert to help debugging */ assert size >= 0: "Payload size cannot be negative"; if (size < 0) { // We don't throw SizeError // because this is only for reception side throw new InternalError(); } BytesArrayTools.write(packet, PAYLOAD_SIZE_POSITION, size); } /** Get payload’s size from a Packet. * @param packet the full Packet received * @return integer representing payload size * @throws SizeError */ protected static int getPayloadSize(byte[] packet) throws SizeError { return BytesArrayTools.readInt(packet, PAYLOAD_SIZE_POSITION); } /** RRCode getter. * @return Request/Response code */ public RequestResponseCode getRequestResponseCode() { return requestResponseCode; } }