private void writeHeader(LogSegment segment, byte[] buf) throws IOException { FileChannel channel = segment.getView().getSecond(); ByteBuffer buffer = ByteBuffer.wrap(buf); while (buffer.hasRemaining()) { channel.write(buffer, 0); } }
/** * Closes the {@code segment} and deletes the backing file. * * @param segment the {@link LogSegment} that needs to be closed and whose backing file needs to * be deleted. * @throws IOException */ private void closeSegmentAndDeleteFile(LogSegment segment) throws IOException { segment.close(); assertFalse("File channel is not closed", segment.getView().getSecond().isOpen()); File segmentFile = new File(tempDir, segment.getName()); assertTrue( "The segment file [" + segmentFile.getAbsolutePath() + "] could not be deleted", segmentFile.delete()); }
/** * Gets a view of the given {@code segment} and verifies the ref count and the data obtained from * the view against {@code expectedRefCount} and {@code dataInSegment} respectively. * * @param segment the {@link LogSegment} to get a view from. * @param writeStartOffset the offset at which write was started on the segment. * @param offset the offset for which a view is required. * @param dataInSegment the entire data in the {@link LogSegment}. * @param expectedRefCount the expected return value of {@link LogSegment#refCount()} once the * view is obtained from the {@code segment} * @throws IOException */ private void getAndVerifyView( LogSegment segment, long writeStartOffset, int offset, byte[] dataInSegment, long expectedRefCount) throws IOException { Random random = new Random(); Pair<File, FileChannel> view = segment.getView(); assertNotNull("File object received in view is null", view.getFirst()); assertNotNull("FileChannel object received in view is null", view.getSecond()); assertEquals("Ref count is not as expected", expectedRefCount, segment.refCount()); int sizeToRead = random.nextInt(dataInSegment.length - offset + 1); ByteBuffer buffer = ByteBuffer.wrap(new byte[sizeToRead]); view.getSecond().read(buffer, writeStartOffset + offset); assertArrayEquals( "Data read from file does not match data written", Arrays.copyOfRange(dataInSegment, offset, offset + sizeToRead), buffer.array()); }
/** * Tests setting end offset - makes sure legal values are set correctly and illegal values are * rejected. * * @throws IOException */ @Test public void endOffsetTest() throws IOException { String segmentName = "log_current"; LogSegment segment = getSegment(segmentName, STANDARD_SEGMENT_SIZE, true); try { long writeStartOffset = segment.getStartOffset(); int segmentSize = 500; appendRandomData(segment, segmentSize); assertEquals( "End offset is not as expected", writeStartOffset + segmentSize, segment.getEndOffset()); // should be able to set end offset to something >= initial offset and <= file size int[] offsetsToSet = {(int) (writeStartOffset), segmentSize / 2, segmentSize}; for (int offset : offsetsToSet) { segment.setEndOffset(offset); assertEquals("End offset is not equal to what was set", offset, segment.getEndOffset()); assertEquals( "File channel positioning is incorrect", offset, segment.getView().getSecond().position()); } // cannot set end offset to illegal values (< initial offset or > file size) int[] invalidOffsets = {(int) (writeStartOffset - 1), (int) (segment.sizeInBytes() + 1)}; for (int offset : invalidOffsets) { try { segment.setEndOffset(offset); fail("Setting log end offset an invalid offset [" + offset + "] should have failed"); } catch (IllegalArgumentException e) { // expected. Nothing to do. } } } finally { closeSegmentAndDeleteFile(segment); } }
private byte[] getHeader(LogSegment segment) throws IOException { FileChannel channel = segment.getView().getSecond(); ByteBuffer header = ByteBuffer.allocate(LogSegment.HEADER_SIZE); channel.read(header, 0); return header.array(); }
/** * Tests for bad construction cases of {@link LogSegment}. * * @throws IOException */ @Test public void badConstructionTest() throws IOException { // try to construct with a file that does not exist. String name = "log_non_existent"; File file = new File(tempDir, name); try { new LogSegment(name, file, STANDARD_SEGMENT_SIZE, metrics, true); fail("Construction should have failed because the backing file does not exist"); } catch (IllegalArgumentException e) { // expected. Nothing to do. } try { new LogSegment(name, file, metrics); fail("Construction should have failed because the backing file does not exist"); } catch (IllegalArgumentException e) { // expected. Nothing to do. } // try to construct with a file that is a directory name = tempDir.getName(); file = new File(tempDir.getParent(), name); try { new LogSegment(name, file, STANDARD_SEGMENT_SIZE, metrics, true); fail("Construction should have failed because the backing file does not exist"); } catch (IllegalArgumentException e) { // expected. Nothing to do. } name = tempDir.getName(); file = new File(tempDir.getParent(), name); try { new LogSegment(name, file, metrics); fail("Construction should have failed because the backing file does not exist"); } catch (IllegalArgumentException e) { // expected. Nothing to do. } // unknown version LogSegment segment = getSegment("dummy_log", STANDARD_SEGMENT_SIZE, true); file = segment.getView().getFirst(); byte[] header = getHeader(segment); byte savedByte = header[0]; // mess with version header[0] = (byte) (header[0] + 10); writeHeader(segment, header); try { new LogSegment(name, file, metrics); fail("Construction should have failed because version is unknown"); } catch (IllegalArgumentException e) { // expected. Nothing to do. } // bad CRC // fix version but mess with data after version header[0] = savedByte; header[2] = header[2] == (byte) 1 ? (byte) 0 : (byte) 1; writeHeader(segment, header); try { new LogSegment(name, file, metrics); fail("Construction should have failed because crc check should have failed"); } catch (IllegalStateException e) { // expected. Nothing to do. } closeSegmentAndDeleteFile(segment); }