package protocolP2P; import protocolP2P.Payload; import protocolP2P.RequestResponseCode; import localException.ProtocolError; import localException.InternalError; import localException.SizeError; import localException.TransmissionError; import tools.BytesArrayTools; /** Representation of payload for load response. * @author Louis Royer * @author Flavien Haas * @author JS Auge * @version 1.0 */ public class FilePart extends Payload { private String filename; private long totalSize; private long offset; private byte[] partialContent; static final private int OFFSET_POSITION = PAYLOAD_START_POSITION; static final private int TOTAL_FILESIZE_POSITION = OFFSET_POSITION + 8; static final private int FILENAME_SIZE_POSITION = TOTAL_FILESIZE_POSITION + 8; static final private int FILENAME_POSITION = FILENAME_SIZE_POSITION + 4; /** Constructor (typically used by server) with informations about file part to send as parameters. * @param filename name of the file to send * @param totalSize total size of the file to send * @param offset where in the file begins the part we are sending * @param partialContent content of the file we send * @throws InternalError */ public FilePart(String filename, long totalSize, long offset, byte[] partialContent) throws InternalError { super(RequestResponseCode.LOAD_RESPONSE); /* asserts to help debugging */ assert totalSize >= 0 : "totalSize cannot be negative"; assert partialContent.length != 0 : "partialContent.length cannot be zero, see RRCode.EMPTY_FILE"; assert totalSize >= partialContent.length : "totalSize must be greater than partialContent.length"; assert offset >= 0 : "offset cannot be negative"; assert filename != null : "filename is required"; if (totalSize < 0 || partialContent.length == 0 || totalSize < partialContent.length || offset < 0 || filename == null) { throw new InternalError(); } this.filename = filename; this.totalSize = totalSize; this.offset = offset; this.partialContent = partialContent; } /** Constructor (typically used by client) with Packet received as parameter. * @param packet the full Packet received * @throws SizeError * @throws InternalError * @throws TransmissionError */ protected FilePart(byte[] packet) throws TransmissionError, SizeError, ProtocolError, InternalError { super(packet); /* assert to help debugging */ assert requestResponseCode == RequestResponseCode.LOAD_RESPONSE : "FilePart subclass is incompatible with this Packet, request/response code must be checked before using this constructor"; /* InternalErrorException */ if (requestResponseCode != RequestResponseCode.LOAD_RESPONSE) { throw new InternalError(); } setOffset(packet); // this can throw SizeError setTotalSize(packet); // this can throw SizeError setFilename(packet); // this can throw ProtocolError, SizeError setPartialContent(packet); // this can throw SizeError } /** 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 { // compute total size int size = FILENAME_POSITION + filename.length() + partialContent.length; byte[] packet = new byte[size + 1]; // java initialize all to zero // set request/response code packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue; // set Payload size setPayloadSize(size - PAYLOAD_START_POSITION, packet); // write offset to Packet BytesArrayTools.write(packet, OFFSET_POSITION, offset); // write totalSize to Packet BytesArrayTools.write(packet, TOTAL_FILESIZE_POSITION, totalSize); // write filename’s size to Packet BytesArrayTools.write(packet, FILENAME_SIZE_POSITION, filename.length()); // write filename to Packet BytesArrayTools.write(packet, filename, FILENAME_POSITION); // write partialContent to Packet BytesArrayTools.write(packet, partialContent, FILENAME_POSITION + filename.length()); return packet; } /** Write from Packet into offset. * @param packet received Packet * @throws SizeError */ private void setOffset(byte[] packet) throws SizeError { offset = BytesArrayTools.readLong(packet, OFFSET_POSITION); } /** Write from Packet into totalSize. * @param packet received Packet * @throws SizeError */ private void setTotalSize(byte[] packet) throws SizeError { totalSize = BytesArrayTools.readLong(packet, TOTAL_FILESIZE_POSITION); } /** Read filename’s size from Packet. * @param packet received Packet * @throws ProtocolError * @throws SizeError * @return filename’s size */ private int getFilenameSize(byte[] packet) throws SizeError, ProtocolError { int size = BytesArrayTools.readInt(packet, FILENAME_SIZE_POSITION); // this can throw SizeError // filename size cannot be zero if (size == 0) { throw new ProtocolError(); } // cannot excess packet size if ((FILENAME_POSITION + size) > (getPayloadSize(packet) + OFFSET_POSITION)) { throw new ProtocolError(); } return size; } /** Write from Packet into filename. * @param packet received Packet * @throws ProtocolError * @throws SizeError * @throws InternalError */ private void setFilename(byte[] packet) throws ProtocolError, SizeError, InternalError { int filenameSize = getFilenameSize(packet); // this can throw ProtocolError or SizeError filename = BytesArrayTools.readString(packet, FILENAME_POSITION, filenameSize); } /** Write from Packet into partialContent. * @param packet received Packet * @throws SizeError * @throws ProtocolError */ private void setPartialContent(byte[] packet) throws ProtocolError, SizeError { int start = FILENAME_POSITION + getFilenameSize(packet); // this can throw SizeError or ProtocolError int end = OFFSET_POSITION + getPayloadSize(packet); // this can throw SizeError partialContent = BytesArrayTools.readByteArray(packet, start, end); } /** partialContent getter. * @return partialcontent */ public byte[] getPartialContent() { return partialContent; } /** filename getter. * @return String */ public String getFilename() { return filename; } /** offset getter. * @return offset */ public long getOffset() { return offset; } /** totalSize getter. * @return totalSize */ public long getTotalSize() { return totalSize; } }