commit
13f41da54f
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,7 +16,9 @@
|
|||
|
||||
package org.springframework.boot.loader.zip;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ClosedByInterruptException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
|
@ -29,31 +31,31 @@ import java.util.function.Supplier;
|
|||
import org.springframework.boot.loader.log.DebugLogger;
|
||||
|
||||
/**
|
||||
* Reference counted {@link DataBlock} implementation backed by a {@link FileChannel} with
|
||||
* Reference counted {@link DataBlock} implementation backed by a {@link File} with
|
||||
* support for slicing.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class FileChannelDataBlock implements CloseableDataBlock {
|
||||
class FileDataBlock implements CloseableDataBlock {
|
||||
|
||||
private static final DebugLogger debug = DebugLogger.get(FileChannelDataBlock.class);
|
||||
private static final DebugLogger debug = DebugLogger.get(FileDataBlock.class);
|
||||
|
||||
static Tracker tracker;
|
||||
static Tracker tracker = Tracker.NONE;
|
||||
|
||||
private final ManagedFileChannel channel;
|
||||
private final FileAccess fileAccess;
|
||||
|
||||
private final long offset;
|
||||
|
||||
private final long size;
|
||||
|
||||
FileChannelDataBlock(Path path) throws IOException {
|
||||
this.channel = new ManagedFileChannel(path);
|
||||
FileDataBlock(Path path) throws IOException {
|
||||
this.fileAccess = new FileAccess(path);
|
||||
this.offset = 0;
|
||||
this.size = Files.size(path);
|
||||
}
|
||||
|
||||
FileChannelDataBlock(ManagedFileChannel channel, long offset, long size) {
|
||||
this.channel = channel;
|
||||
FileDataBlock(FileAccess fileAccess, long offset, long size) {
|
||||
this.fileAccess = fileAccess;
|
||||
this.offset = offset;
|
||||
this.size = size;
|
||||
}
|
||||
|
@ -78,7 +80,7 @@ class FileChannelDataBlock implements CloseableDataBlock {
|
|||
originalDestinationLimit = dst.limit();
|
||||
dst.limit(dst.position() + remaining);
|
||||
}
|
||||
int result = this.channel.read(dst, this.offset + pos);
|
||||
int result = this.fileAccess.read(dst, this.offset + pos);
|
||||
if (originalDestinationLimit != -1) {
|
||||
dst.limit(originalDestinationLimit);
|
||||
}
|
||||
|
@ -91,7 +93,7 @@ class FileChannelDataBlock implements CloseableDataBlock {
|
|||
* @throws IOException on I/O error
|
||||
*/
|
||||
void open() throws IOException {
|
||||
this.channel.open();
|
||||
this.fileAccess.open();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,7 +103,7 @@ class FileChannelDataBlock implements CloseableDataBlock {
|
|||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.channel.close();
|
||||
this.fileAccess.close();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,30 +113,30 @@ class FileChannelDataBlock implements CloseableDataBlock {
|
|||
* @throws E if the channel is closed
|
||||
*/
|
||||
<E extends Exception> void ensureOpen(Supplier<E> exceptionSupplier) throws E {
|
||||
this.channel.ensureOpen(exceptionSupplier);
|
||||
this.fileAccess.ensureOpen(exceptionSupplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link FileChannelDataBlock} slice providing access to a subset of the
|
||||
* data. The caller is responsible for calling {@link #open()} and {@link #close()} on
|
||||
* the returned block.
|
||||
* Return a new {@link FileDataBlock} slice providing access to a subset of the data.
|
||||
* The caller is responsible for calling {@link #open()} and {@link #close()} on the
|
||||
* returned block.
|
||||
* @param offset the start offset for the slice relative to this block
|
||||
* @return a new {@link FileChannelDataBlock} instance
|
||||
* @return a new {@link FileDataBlock} instance
|
||||
* @throws IOException on I/O error
|
||||
*/
|
||||
FileChannelDataBlock slice(long offset) throws IOException {
|
||||
FileDataBlock slice(long offset) throws IOException {
|
||||
return slice(offset, this.size - offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link FileChannelDataBlock} slice providing access to a subset of the
|
||||
* data. The caller is responsible for calling {@link #open()} and {@link #close()} on
|
||||
* the returned block.
|
||||
* Return a new {@link FileDataBlock} slice providing access to a subset of the data.
|
||||
* The caller is responsible for calling {@link #open()} and {@link #close()} on the
|
||||
* returned block.
|
||||
* @param offset the start offset for the slice relative to this block
|
||||
* @param size the size of the new slice
|
||||
* @return a new {@link FileChannelDataBlock} instance
|
||||
* @return a new {@link FileDataBlock} instance
|
||||
*/
|
||||
FileChannelDataBlock slice(long offset, long size) {
|
||||
FileDataBlock slice(long offset, long size) {
|
||||
if (offset == 0 && size == this.size) {
|
||||
return this;
|
||||
}
|
||||
|
@ -144,14 +146,14 @@ class FileChannelDataBlock implements CloseableDataBlock {
|
|||
if (size < 0 || offset + size > this.size) {
|
||||
throw new IllegalArgumentException("Size must not be negative and must be within bounds");
|
||||
}
|
||||
debug.log("Slicing %s at %s with size %s", this.channel, offset, size);
|
||||
return new FileChannelDataBlock(this.channel, this.offset + offset, size);
|
||||
debug.log("Slicing %s at %s with size %s", this.fileAccess, offset, size);
|
||||
return new FileDataBlock(this.fileAccess, this.offset + offset, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages access to underlying {@link FileChannel}.
|
||||
*/
|
||||
static class ManagedFileChannel {
|
||||
static class FileAccess {
|
||||
|
||||
static final int BUFFER_SIZE = 1024 * 10;
|
||||
|
||||
|
@ -161,6 +163,10 @@ class FileChannelDataBlock implements CloseableDataBlock {
|
|||
|
||||
private FileChannel fileChannel;
|
||||
|
||||
private boolean fileChannelInterrupted;
|
||||
|
||||
private RandomAccessFile randomAccessFile;
|
||||
|
||||
private ByteBuffer buffer;
|
||||
|
||||
private long bufferPosition = -1;
|
||||
|
@ -169,7 +175,7 @@ class FileChannelDataBlock implements CloseableDataBlock {
|
|||
|
||||
private final Object lock = new Object();
|
||||
|
||||
ManagedFileChannel(Path path) {
|
||||
FileAccess(Path path) {
|
||||
if (!Files.isRegularFile(path)) {
|
||||
throw new IllegalArgumentException(path + " must be a regular file");
|
||||
}
|
||||
|
@ -193,34 +199,45 @@ class FileChannelDataBlock implements CloseableDataBlock {
|
|||
}
|
||||
|
||||
private void fillBuffer(long position) throws IOException {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
boolean interrupted = (i != 0) ? Thread.interrupted() : false;
|
||||
try {
|
||||
this.buffer.clear();
|
||||
this.bufferSize = this.fileChannel.read(this.buffer, position);
|
||||
this.bufferPosition = position;
|
||||
return;
|
||||
}
|
||||
catch (ClosedByInterruptException ex) {
|
||||
repairFileChannel();
|
||||
}
|
||||
finally {
|
||||
if (interrupted) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
fillBufferUsingRandomAccessFile(position);
|
||||
return;
|
||||
}
|
||||
throw new ClosedByInterruptException();
|
||||
try {
|
||||
if (this.fileChannelInterrupted) {
|
||||
repairFileChannel();
|
||||
this.fileChannelInterrupted = false;
|
||||
}
|
||||
this.buffer.clear();
|
||||
this.bufferSize = this.fileChannel.read(this.buffer, position);
|
||||
this.bufferPosition = position;
|
||||
}
|
||||
catch (ClosedByInterruptException ex) {
|
||||
this.fileChannelInterrupted = true;
|
||||
fillBufferUsingRandomAccessFile(position);
|
||||
}
|
||||
}
|
||||
|
||||
private void fillBufferUsingRandomAccessFile(long position) throws IOException {
|
||||
if (this.randomAccessFile == null) {
|
||||
this.randomAccessFile = new RandomAccessFile(this.path.toFile(), "r");
|
||||
tracker.openedFileChannel(this.path);
|
||||
}
|
||||
byte[] bytes = new byte[BUFFER_SIZE];
|
||||
this.randomAccessFile.seek(position);
|
||||
int len = this.randomAccessFile.read(bytes);
|
||||
this.buffer.clear();
|
||||
if (len > 0) {
|
||||
this.buffer.put(bytes, 0, len);
|
||||
}
|
||||
this.bufferSize = len;
|
||||
this.bufferPosition = position;
|
||||
}
|
||||
|
||||
private void repairFileChannel() throws IOException {
|
||||
if (tracker != null) {
|
||||
tracker.closedFileChannel(this.path, this.fileChannel);
|
||||
}
|
||||
tracker.closedFileChannel(this.path);
|
||||
this.fileChannel = FileChannel.open(this.path, StandardOpenOption.READ);
|
||||
if (tracker != null) {
|
||||
tracker.openedFileChannel(this.path, this.fileChannel);
|
||||
}
|
||||
tracker.openedFileChannel(this.path);
|
||||
}
|
||||
|
||||
void open() throws IOException {
|
||||
|
@ -229,9 +246,7 @@ class FileChannelDataBlock implements CloseableDataBlock {
|
|||
debug.log("Opening '%s'", this.path);
|
||||
this.fileChannel = FileChannel.open(this.path, StandardOpenOption.READ);
|
||||
this.buffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
|
||||
if (tracker != null) {
|
||||
tracker.openedFileChannel(this.path, this.fileChannel);
|
||||
}
|
||||
tracker.openedFileChannel(this.path);
|
||||
}
|
||||
this.referenceCount++;
|
||||
debug.log("Reference count for '%s' incremented to %s", this.path, this.referenceCount);
|
||||
|
@ -250,10 +265,13 @@ class FileChannelDataBlock implements CloseableDataBlock {
|
|||
this.bufferPosition = -1;
|
||||
this.bufferSize = 0;
|
||||
this.fileChannel.close();
|
||||
if (tracker != null) {
|
||||
tracker.closedFileChannel(this.path, this.fileChannel);
|
||||
}
|
||||
tracker.closedFileChannel(this.path);
|
||||
this.fileChannel = null;
|
||||
if (this.randomAccessFile != null) {
|
||||
this.randomAccessFile.close();
|
||||
tracker.closedFileChannel(this.path);
|
||||
this.randomAccessFile = null;
|
||||
}
|
||||
}
|
||||
debug.log("Reference count for '%s' decremented to %s", this.path, this.referenceCount);
|
||||
}
|
||||
|
@ -261,7 +279,7 @@ class FileChannelDataBlock implements CloseableDataBlock {
|
|||
|
||||
<E extends Exception> void ensureOpen(Supplier<E> exceptionSupplier) throws E {
|
||||
synchronized (this.lock) {
|
||||
if (this.referenceCount == 0 || !this.fileChannel.isOpen()) {
|
||||
if (this.referenceCount == 0) {
|
||||
throw exceptionSupplier.get();
|
||||
}
|
||||
}
|
||||
|
@ -279,9 +297,21 @@ class FileChannelDataBlock implements CloseableDataBlock {
|
|||
*/
|
||||
interface Tracker {
|
||||
|
||||
void openedFileChannel(Path path, FileChannel fileChannel);
|
||||
Tracker NONE = new Tracker() {
|
||||
|
||||
void closedFileChannel(Path path, FileChannel fileChannel);
|
||||
@Override
|
||||
public void openedFileChannel(Path path) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closedFileChannel(Path path) {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void openedFileChannel(Path path);
|
||||
|
||||
void closedFileChannel(Path path);
|
||||
|
||||
}
|
||||
|
|
@ -74,7 +74,7 @@ public final class ZipContent implements Closeable {
|
|||
|
||||
private final Kind kind;
|
||||
|
||||
private final FileChannelDataBlock data;
|
||||
private final FileDataBlock data;
|
||||
|
||||
private final long centralDirectoryPos;
|
||||
|
||||
|
@ -96,7 +96,7 @@ public final class ZipContent implements Closeable {
|
|||
|
||||
private SoftReference<Map<Class<?>, Object>> info;
|
||||
|
||||
private ZipContent(Source source, Kind kind, FileChannelDataBlock data, long centralDirectoryPos, long commentPos,
|
||||
private ZipContent(Source source, Kind kind, FileDataBlock data, long centralDirectoryPos, long commentPos,
|
||||
long commentLength, int[] lookupIndexes, int[] nameHashLookups, int[] relativeCentralDirectoryOffsetLookups,
|
||||
NameOffsetLookups nameOffsetLookups, boolean hasJarSignatureFile) {
|
||||
this.source = source;
|
||||
|
@ -449,7 +449,7 @@ public final class ZipContent implements Closeable {
|
|||
|
||||
private final Source source;
|
||||
|
||||
private final FileChannelDataBlock data;
|
||||
private final FileDataBlock data;
|
||||
|
||||
private final long centralDirectoryPos;
|
||||
|
||||
|
@ -463,8 +463,7 @@ public final class ZipContent implements Closeable {
|
|||
|
||||
private int cursor;
|
||||
|
||||
private Loader(Source source, Entry directoryEntry, FileChannelDataBlock data, long centralDirectoryPos,
|
||||
int maxSize) {
|
||||
private Loader(Source source, Entry directoryEntry, FileDataBlock data, long centralDirectoryPos, int maxSize) {
|
||||
this.source = source;
|
||||
this.data = data;
|
||||
this.centralDirectoryPos = centralDirectoryPos;
|
||||
|
@ -561,7 +560,7 @@ public final class ZipContent implements Closeable {
|
|||
|
||||
private static ZipContent loadNonNested(Source source) throws IOException {
|
||||
debug.log("Loading non-nested zip '%s'", source.path());
|
||||
return openAndLoad(source, Kind.ZIP, new FileChannelDataBlock(source.path()));
|
||||
return openAndLoad(source, Kind.ZIP, new FileDataBlock(source.path()));
|
||||
}
|
||||
|
||||
private static ZipContent loadNestedZip(Source source, Entry entry) throws IOException {
|
||||
|
@ -573,7 +572,7 @@ public final class ZipContent implements Closeable {
|
|||
return openAndLoad(source, Kind.NESTED_ZIP, entry.getContent());
|
||||
}
|
||||
|
||||
private static ZipContent openAndLoad(Source source, Kind kind, FileChannelDataBlock data) throws IOException {
|
||||
private static ZipContent openAndLoad(Source source, Kind kind, FileDataBlock data) throws IOException {
|
||||
try {
|
||||
data.open();
|
||||
return loadContent(source, kind, data);
|
||||
|
@ -584,7 +583,7 @@ public final class ZipContent implements Closeable {
|
|||
}
|
||||
}
|
||||
|
||||
private static ZipContent loadContent(Source source, Kind kind, FileChannelDataBlock data) throws IOException {
|
||||
private static ZipContent loadContent(Source source, Kind kind, FileDataBlock data) throws IOException {
|
||||
ZipEndOfCentralDirectoryRecord.Located locatedEocd = ZipEndOfCentralDirectoryRecord.load(data);
|
||||
ZipEndOfCentralDirectoryRecord eocd = locatedEocd.endOfCentralDirectoryRecord();
|
||||
long eocdPos = locatedEocd.pos();
|
||||
|
@ -634,7 +633,7 @@ public final class ZipContent implements Closeable {
|
|||
* @return the offset within the data where the archive begins
|
||||
* @throws IOException on I/O error
|
||||
*/
|
||||
private static long getStartOfZipContent(FileChannelDataBlock data, ZipEndOfCentralDirectoryRecord eocd,
|
||||
private static long getStartOfZipContent(FileDataBlock data, ZipEndOfCentralDirectoryRecord eocd,
|
||||
Zip64EndOfCentralDirectoryRecord zip64Eocd) throws IOException {
|
||||
long specifiedOffsetToStartOfCentralDirectory = (zip64Eocd != null)
|
||||
? zip64Eocd.offsetToStartOfCentralDirectory() : eocd.offsetToStartOfCentralDirectory();
|
||||
|
@ -699,7 +698,7 @@ public final class ZipContent implements Closeable {
|
|||
|
||||
private volatile String name;
|
||||
|
||||
private volatile FileChannelDataBlock content;
|
||||
private volatile FileDataBlock content;
|
||||
|
||||
/**
|
||||
* Create a new {@link Entry} instance.
|
||||
|
@ -789,13 +788,13 @@ public final class ZipContent implements Closeable {
|
|||
* @throws IOException on I/O error
|
||||
*/
|
||||
public CloseableDataBlock openContent() throws IOException {
|
||||
FileChannelDataBlock content = getContent();
|
||||
FileDataBlock content = getContent();
|
||||
content.open();
|
||||
return content;
|
||||
}
|
||||
|
||||
private FileChannelDataBlock getContent() throws IOException {
|
||||
FileChannelDataBlock content = this.content;
|
||||
private FileDataBlock getContent() throws IOException {
|
||||
FileDataBlock content = this.content;
|
||||
if (content == null) {
|
||||
int pos = this.centralRecord.offsetToLocalHeader();
|
||||
checkNotZip64Extended(pos);
|
||||
|
|
|
@ -303,7 +303,7 @@ class NestedJarFileTests {
|
|||
Cleanable cleanable = mock(Cleanable.class);
|
||||
given(cleaner.register(any(), action.capture())).willReturn(cleanable);
|
||||
try (NestedJarFile jar = new NestedJarFile(this.file, null, null, false, cleaner)) {
|
||||
Object channel = Extractors.byName("resources.zipContent.data.channel").apply(jar);
|
||||
Object channel = Extractors.byName("resources.zipContent.data.fileAccess").apply(jar);
|
||||
assertThat(channel).extracting("referenceCount").isEqualTo(1);
|
||||
action.getValue().run();
|
||||
assertThat(channel).extracting("referenceCount").isEqualTo(0);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -25,8 +25,8 @@ import java.lang.annotation.Target;
|
|||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
/**
|
||||
* Annotation that can be added to tests to assert that {@link FileChannelDataBlock} files
|
||||
* are not left open.
|
||||
* Annotation that can be added to tests to assert that {@link FileDataBlock} files are
|
||||
* not left open.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.springframework.boot.loader.zip;
|
|||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.Cleaner.Cleanable;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
|
@ -31,7 +30,7 @@ import org.junit.jupiter.api.extension.BeforeEachCallback;
|
|||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
|
||||
import org.springframework.boot.loader.ref.DefaultCleanerTracking;
|
||||
import org.springframework.boot.loader.zip.FileChannelDataBlock.Tracker;
|
||||
import org.springframework.boot.loader.zip.FileDataBlock.Tracker;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
@ -45,14 +44,14 @@ class AssertFileChannelDataBlocksClosedExtension implements BeforeEachCallback,
|
|||
@Override
|
||||
public void beforeEach(ExtensionContext context) throws Exception {
|
||||
tracker.clear();
|
||||
FileChannelDataBlock.tracker = tracker;
|
||||
FileDataBlock.tracker = tracker;
|
||||
DefaultCleanerTracking.set(tracker::addedCleanable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterEach(ExtensionContext context) throws Exception {
|
||||
tracker.assertAllClosed();
|
||||
FileChannelDataBlock.tracker = null;
|
||||
FileDataBlock.tracker = Tracker.NONE;
|
||||
}
|
||||
|
||||
private static final class OpenFilesTracker implements Tracker {
|
||||
|
@ -64,12 +63,12 @@ class AssertFileChannelDataBlocksClosedExtension implements BeforeEachCallback,
|
|||
private final List<Closeable> close = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void openedFileChannel(Path path, FileChannel fileChannel) {
|
||||
public void openedFileChannel(Path path) {
|
||||
this.paths.add(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closedFileChannel(Path path, FileChannel fileChannel) {
|
||||
public void closedFileChannel(Path path) {
|
||||
this.paths.remove(path);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,10 +16,10 @@
|
|||
|
||||
package org.springframework.boot.loader.zip;
|
||||
|
||||
import org.springframework.boot.loader.zip.FileChannelDataBlock.ManagedFileChannel;
|
||||
import org.springframework.boot.loader.zip.FileDataBlock.FileAccess;
|
||||
|
||||
/**
|
||||
* Test access to {@link ManagedFileChannel} details.
|
||||
* Test access to {@link FileAccess} details.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
|
@ -28,6 +28,6 @@ public final class FileChannelDataBlockManagedFileChannel {
|
|||
private FileChannelDataBlockManagedFileChannel() {
|
||||
}
|
||||
|
||||
public static int BUFFER_SIZE = FileChannelDataBlock.ManagedFileChannel.BUFFER_SIZE;
|
||||
public static int BUFFER_SIZE = FileDataBlock.FileAccess.BUFFER_SIZE;
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -19,7 +19,6 @@ package org.springframework.boot.loader.zip;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
|
@ -28,17 +27,17 @@ import org.junit.jupiter.api.BeforeEach;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import org.springframework.boot.loader.zip.FileChannelDataBlock.Tracker;
|
||||
import org.springframework.boot.loader.zip.FileDataBlock.Tracker;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link FileChannelDataBlock}.
|
||||
* Tests for {@link FileDataBlock}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class FileChannelDataBlockTests {
|
||||
class FileDataBlockTests {
|
||||
|
||||
private static final byte[] CONTENT = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
|
||||
|
||||
|
@ -55,19 +54,19 @@ class FileChannelDataBlockTests {
|
|||
|
||||
@AfterEach
|
||||
void resetTracker() {
|
||||
FileChannelDataBlock.tracker = null;
|
||||
FileDataBlock.tracker = Tracker.NONE;
|
||||
}
|
||||
|
||||
@Test
|
||||
void sizeReturnsFileSize() throws IOException {
|
||||
try (FileChannelDataBlock block = createAndOpenBlock()) {
|
||||
try (FileDataBlock block = createAndOpenBlock()) {
|
||||
assertThat(block.size()).isEqualTo(CONTENT.length);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void readReadsFile() throws IOException {
|
||||
try (FileChannelDataBlock block = createAndOpenBlock()) {
|
||||
try (FileDataBlock block = createAndOpenBlock()) {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(CONTENT.length);
|
||||
assertThat(block.read(buffer, 0)).isEqualTo(6);
|
||||
assertThat(buffer.array()).containsExactly(CONTENT);
|
||||
|
@ -76,7 +75,8 @@ class FileChannelDataBlockTests {
|
|||
|
||||
@Test
|
||||
void readReadsFileWhenThreadHasBeenInterrupted() throws IOException {
|
||||
try (FileChannelDataBlock block = createAndOpenBlock()) {
|
||||
Files.write(this.tempFile.toPath(), CONTENT);
|
||||
try (FileDataBlock block = createAndOpenBlock()) {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(CONTENT.length);
|
||||
Thread.currentThread().interrupt();
|
||||
assertThat(block.read(buffer, 0)).isEqualTo(6);
|
||||
|
@ -89,7 +89,7 @@ class FileChannelDataBlockTests {
|
|||
|
||||
@Test
|
||||
void readDoesNotReadPastEndOfFile() throws IOException {
|
||||
try (FileChannelDataBlock block = createAndOpenBlock()) {
|
||||
try (FileDataBlock block = createAndOpenBlock()) {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(CONTENT.length);
|
||||
assertThat(block.read(buffer, 2)).isEqualTo(4);
|
||||
assertThat(buffer.array()).containsExactly(0x02, 0x03, 0x04, 0x05, 0x0, 0x0);
|
||||
|
@ -98,7 +98,7 @@ class FileChannelDataBlockTests {
|
|||
|
||||
@Test
|
||||
void readWhenPosAtSizeReturnsMinusOne() throws IOException {
|
||||
try (FileChannelDataBlock block = createAndOpenBlock()) {
|
||||
try (FileDataBlock block = createAndOpenBlock()) {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(CONTENT.length);
|
||||
assertThat(block.read(buffer, 6)).isEqualTo(-1);
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ class FileChannelDataBlockTests {
|
|||
|
||||
@Test
|
||||
void readWhenPosOverSizeReturnsMinusOne() throws IOException {
|
||||
try (FileChannelDataBlock block = createAndOpenBlock()) {
|
||||
try (FileDataBlock block = createAndOpenBlock()) {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(CONTENT.length);
|
||||
assertThat(block.read(buffer, 7)).isEqualTo(-1);
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ class FileChannelDataBlockTests {
|
|||
|
||||
@Test
|
||||
void readWhenPosIsNegativeThrowsException() throws IOException {
|
||||
try (FileChannelDataBlock block = createAndOpenBlock()) {
|
||||
try (FileDataBlock block = createAndOpenBlock()) {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(CONTENT.length);
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> block.read(buffer, -1));
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ class FileChannelDataBlockTests {
|
|||
|
||||
@Test
|
||||
void sliceWhenOffsetIsNegativeThrowsException() throws IOException {
|
||||
try (FileChannelDataBlock block = createAndOpenBlock()) {
|
||||
try (FileDataBlock block = createAndOpenBlock()) {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> block.slice(-1, 0))
|
||||
.withMessage("Offset must not be negative");
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ class FileChannelDataBlockTests {
|
|||
|
||||
@Test
|
||||
void sliceWhenSizeIsNegativeThrowsException() throws IOException {
|
||||
try (FileChannelDataBlock block = createAndOpenBlock()) {
|
||||
try (FileDataBlock block = createAndOpenBlock()) {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> block.slice(0, -1))
|
||||
.withMessage("Size must not be negative and must be within bounds");
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ class FileChannelDataBlockTests {
|
|||
|
||||
@Test
|
||||
void sliceWhenSizeIsOutOfBoundsThrowsException() throws IOException {
|
||||
try (FileChannelDataBlock block = createAndOpenBlock()) {
|
||||
try (FileDataBlock block = createAndOpenBlock()) {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> block.slice(2, 5))
|
||||
.withMessage("Size must not be negative and must be within bounds");
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ class FileChannelDataBlockTests {
|
|||
|
||||
@Test
|
||||
void sliceReturnsSlice() throws IOException {
|
||||
try (FileChannelDataBlock slice = createAndOpenBlock().slice(1, 4)) {
|
||||
try (FileDataBlock slice = createAndOpenBlock().slice(1, 4)) {
|
||||
assertThat(slice.size()).isEqualTo(4);
|
||||
ByteBuffer buffer = ByteBuffer.allocate(4);
|
||||
assertThat(slice.read(buffer, 0)).isEqualTo(4);
|
||||
|
@ -157,72 +157,72 @@ class FileChannelDataBlockTests {
|
|||
@Test
|
||||
void openAndCloseHandleReferenceCounting() throws IOException {
|
||||
TestTracker tracker = new TestTracker();
|
||||
FileChannelDataBlock.tracker = tracker;
|
||||
FileChannelDataBlock block = createBlock();
|
||||
assertThat(block).extracting("channel.referenceCount").isEqualTo(0);
|
||||
FileDataBlock.tracker = tracker;
|
||||
FileDataBlock block = createBlock();
|
||||
assertThat(block).extracting("fileAccess.referenceCount").isEqualTo(0);
|
||||
tracker.assertOpenCloseCounts(0, 0);
|
||||
block.open();
|
||||
assertThat(block).extracting("channel.referenceCount").isEqualTo(1);
|
||||
assertThat(block).extracting("fileAccess.referenceCount").isEqualTo(1);
|
||||
tracker.assertOpenCloseCounts(1, 0);
|
||||
block.open();
|
||||
assertThat(block).extracting("channel.referenceCount").isEqualTo(2);
|
||||
assertThat(block).extracting("fileAccess.referenceCount").isEqualTo(2);
|
||||
tracker.assertOpenCloseCounts(1, 0);
|
||||
block.close();
|
||||
assertThat(block).extracting("channel.referenceCount").isEqualTo(1);
|
||||
assertThat(block).extracting("fileAccess.referenceCount").isEqualTo(1);
|
||||
tracker.assertOpenCloseCounts(1, 0);
|
||||
block.close();
|
||||
assertThat(block).extracting("channel.referenceCount").isEqualTo(0);
|
||||
assertThat(block).extracting("fileAccess.referenceCount").isEqualTo(0);
|
||||
tracker.assertOpenCloseCounts(1, 1);
|
||||
block.open();
|
||||
assertThat(block).extracting("channel.referenceCount").isEqualTo(1);
|
||||
assertThat(block).extracting("fileAccess.referenceCount").isEqualTo(1);
|
||||
tracker.assertOpenCloseCounts(2, 1);
|
||||
block.close();
|
||||
assertThat(block).extracting("channel.referenceCount").isEqualTo(0);
|
||||
assertThat(block).extracting("fileAccess.referenceCount").isEqualTo(0);
|
||||
tracker.assertOpenCloseCounts(2, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void openAndCloseSliceHandleReferenceCounting() throws IOException {
|
||||
TestTracker tracker = new TestTracker();
|
||||
FileChannelDataBlock.tracker = tracker;
|
||||
FileChannelDataBlock block = createBlock();
|
||||
FileChannelDataBlock slice = block.slice(1, 4);
|
||||
assertThat(block).extracting("channel.referenceCount").isEqualTo(0);
|
||||
FileDataBlock.tracker = tracker;
|
||||
FileDataBlock block = createBlock();
|
||||
FileDataBlock slice = block.slice(1, 4);
|
||||
assertThat(block).extracting("fileAccess.referenceCount").isEqualTo(0);
|
||||
tracker.assertOpenCloseCounts(0, 0);
|
||||
block.open();
|
||||
assertThat(block).extracting("channel.referenceCount").isEqualTo(1);
|
||||
assertThat(block).extracting("fileAccess.referenceCount").isEqualTo(1);
|
||||
tracker.assertOpenCloseCounts(1, 0);
|
||||
slice.open();
|
||||
assertThat(slice).extracting("channel.referenceCount").isEqualTo(2);
|
||||
assertThat(slice).extracting("fileAccess.referenceCount").isEqualTo(2);
|
||||
tracker.assertOpenCloseCounts(1, 0);
|
||||
slice.open();
|
||||
assertThat(slice).extracting("channel.referenceCount").isEqualTo(3);
|
||||
assertThat(slice).extracting("fileAccess.referenceCount").isEqualTo(3);
|
||||
tracker.assertOpenCloseCounts(1, 0);
|
||||
slice.close();
|
||||
assertThat(slice).extracting("channel.referenceCount").isEqualTo(2);
|
||||
assertThat(slice).extracting("fileAccess.referenceCount").isEqualTo(2);
|
||||
tracker.assertOpenCloseCounts(1, 0);
|
||||
slice.close();
|
||||
assertThat(slice).extracting("channel.referenceCount").isEqualTo(1);
|
||||
assertThat(slice).extracting("fileAccess.referenceCount").isEqualTo(1);
|
||||
tracker.assertOpenCloseCounts(1, 0);
|
||||
block.close();
|
||||
assertThat(block).extracting("channel.referenceCount").isEqualTo(0);
|
||||
assertThat(block).extracting("fileAccess.referenceCount").isEqualTo(0);
|
||||
tracker.assertOpenCloseCounts(1, 1);
|
||||
slice.open();
|
||||
assertThat(slice).extracting("channel.referenceCount").isEqualTo(1);
|
||||
assertThat(slice).extracting("fileAccess.referenceCount").isEqualTo(1);
|
||||
tracker.assertOpenCloseCounts(2, 1);
|
||||
slice.close();
|
||||
assertThat(slice).extracting("channel.referenceCount").isEqualTo(0);
|
||||
assertThat(slice).extracting("fileAccess.referenceCount").isEqualTo(0);
|
||||
tracker.assertOpenCloseCounts(2, 2);
|
||||
}
|
||||
|
||||
private FileChannelDataBlock createAndOpenBlock() throws IOException {
|
||||
FileChannelDataBlock block = createBlock();
|
||||
private FileDataBlock createAndOpenBlock() throws IOException {
|
||||
FileDataBlock block = createBlock();
|
||||
block.open();
|
||||
return block;
|
||||
}
|
||||
|
||||
private FileChannelDataBlock createBlock() throws IOException {
|
||||
return new FileChannelDataBlock(this.tempFile.toPath());
|
||||
private FileDataBlock createBlock() throws IOException {
|
||||
return new FileDataBlock(this.tempFile.toPath());
|
||||
}
|
||||
|
||||
static class TestTracker implements Tracker {
|
||||
|
@ -232,12 +232,12 @@ class FileChannelDataBlockTests {
|
|||
private int closeCount;
|
||||
|
||||
@Override
|
||||
public void openedFileChannel(Path path, FileChannel fileChannel) {
|
||||
public void openedFileChannel(Path path) {
|
||||
this.openCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closedFileChannel(Path path, FileChannel fileChannel) {
|
||||
public void closedFileChannel(Path path) {
|
||||
this.closeCount++;
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -58,7 +58,7 @@ class VirtualZipDataBlockTests {
|
|||
|
||||
@Test
|
||||
void createContainsValidZipContent() throws IOException {
|
||||
FileChannelDataBlock data = new FileChannelDataBlock(this.file.toPath());
|
||||
FileDataBlock data = new FileDataBlock(this.file.toPath());
|
||||
data.open();
|
||||
List<ZipCentralDirectoryFileHeaderRecord> centralRecords = new ArrayList<>();
|
||||
List<Long> centralRecordPositions = new ArrayList<>();
|
||||
|
|
Loading…
Reference in New Issue