Skip to content

Commit 13f41da

Browse files
committed
Merge branch '3.2.x'
Closes gh-40379
2 parents bac736d + 9b0593e commit 13f41da

File tree

8 files changed

+159
-131
lines changed

8 files changed

+159
-131
lines changed
Lines changed: 88 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,7 +16,9 @@
1616

1717
package org.springframework.boot.loader.zip;
1818

19+
import java.io.File;
1920
import java.io.IOException;
21+
import java.io.RandomAccessFile;
2022
import java.nio.ByteBuffer;
2123
import java.nio.channels.ClosedByInterruptException;
2224
import java.nio.channels.ClosedChannelException;
@@ -29,31 +31,31 @@
2931
import org.springframework.boot.loader.log.DebugLogger;
3032

3133
/**
32-
* Reference counted {@link DataBlock} implementation backed by a {@link FileChannel} with
34+
* Reference counted {@link DataBlock} implementation backed by a {@link File} with
3335
* support for slicing.
3436
*
3537
* @author Phillip Webb
3638
*/
37-
class FileChannelDataBlock implements CloseableDataBlock {
39+
class FileDataBlock implements CloseableDataBlock {
3840

39-
private static final DebugLogger debug = DebugLogger.get(FileChannelDataBlock.class);
41+
private static final DebugLogger debug = DebugLogger.get(FileDataBlock.class);
4042

41-
static Tracker tracker;
43+
static Tracker tracker = Tracker.NONE;
4244

43-
private final ManagedFileChannel channel;
45+
private final FileAccess fileAccess;
4446

4547
private final long offset;
4648

4749
private final long size;
4850

49-
FileChannelDataBlock(Path path) throws IOException {
50-
this.channel = new ManagedFileChannel(path);
51+
FileDataBlock(Path path) throws IOException {
52+
this.fileAccess = new FileAccess(path);
5153
this.offset = 0;
5254
this.size = Files.size(path);
5355
}
5456

55-
FileChannelDataBlock(ManagedFileChannel channel, long offset, long size) {
56-
this.channel = channel;
57+
FileDataBlock(FileAccess fileAccess, long offset, long size) {
58+
this.fileAccess = fileAccess;
5759
this.offset = offset;
5860
this.size = size;
5961
}
@@ -78,7 +80,7 @@ public int read(ByteBuffer dst, long pos) throws IOException {
7880
originalDestinationLimit = dst.limit();
7981
dst.limit(dst.position() + remaining);
8082
}
81-
int result = this.channel.read(dst, this.offset + pos);
83+
int result = this.fileAccess.read(dst, this.offset + pos);
8284
if (originalDestinationLimit != -1) {
8385
dst.limit(originalDestinationLimit);
8486
}
@@ -91,7 +93,7 @@ public int read(ByteBuffer dst, long pos) throws IOException {
9193
* @throws IOException on I/O error
9294
*/
9395
void open() throws IOException {
94-
this.channel.open();
96+
this.fileAccess.open();
9597
}
9698

9799
/**
@@ -101,7 +103,7 @@ void open() throws IOException {
101103
*/
102104
@Override
103105
public void close() throws IOException {
104-
this.channel.close();
106+
this.fileAccess.close();
105107
}
106108

107109
/**
@@ -111,30 +113,30 @@ public void close() throws IOException {
111113
* @throws E if the channel is closed
112114
*/
113115
<E extends Exception> void ensureOpen(Supplier<E> exceptionSupplier) throws E {
114-
this.channel.ensureOpen(exceptionSupplier);
116+
this.fileAccess.ensureOpen(exceptionSupplier);
115117
}
116118

117119
/**
118-
* Return a new {@link FileChannelDataBlock} slice providing access to a subset of the
119-
* data. The caller is responsible for calling {@link #open()} and {@link #close()} on
120-
* the returned block.
120+
* Return a new {@link FileDataBlock} slice providing access to a subset of the data.
121+
* The caller is responsible for calling {@link #open()} and {@link #close()} on the
122+
* returned block.
121123
* @param offset the start offset for the slice relative to this block
122-
* @return a new {@link FileChannelDataBlock} instance
124+
* @return a new {@link FileDataBlock} instance
123125
* @throws IOException on I/O error
124126
*/
125-
FileChannelDataBlock slice(long offset) throws IOException {
127+
FileDataBlock slice(long offset) throws IOException {
126128
return slice(offset, this.size - offset);
127129
}
128130

129131
/**
130-
* Return a new {@link FileChannelDataBlock} slice providing access to a subset of the
131-
* data. The caller is responsible for calling {@link #open()} and {@link #close()} on
132-
* the returned block.
132+
* Return a new {@link FileDataBlock} slice providing access to a subset of the data.
133+
* The caller is responsible for calling {@link #open()} and {@link #close()} on the
134+
* returned block.
133135
* @param offset the start offset for the slice relative to this block
134136
* @param size the size of the new slice
135-
* @return a new {@link FileChannelDataBlock} instance
137+
* @return a new {@link FileDataBlock} instance
136138
*/
137-
FileChannelDataBlock slice(long offset, long size) {
139+
FileDataBlock slice(long offset, long size) {
138140
if (offset == 0 && size == this.size) {
139141
return this;
140142
}
@@ -144,14 +146,14 @@ FileChannelDataBlock slice(long offset, long size) {
144146
if (size < 0 || offset + size > this.size) {
145147
throw new IllegalArgumentException("Size must not be negative and must be within bounds");
146148
}
147-
debug.log("Slicing %s at %s with size %s", this.channel, offset, size);
148-
return new FileChannelDataBlock(this.channel, this.offset + offset, size);
149+
debug.log("Slicing %s at %s with size %s", this.fileAccess, offset, size);
150+
return new FileDataBlock(this.fileAccess, this.offset + offset, size);
149151
}
150152

151153
/**
152154
* Manages access to underlying {@link FileChannel}.
153155
*/
154-
static class ManagedFileChannel {
156+
static class FileAccess {
155157

156158
static final int BUFFER_SIZE = 1024 * 10;
157159

@@ -161,6 +163,10 @@ static class ManagedFileChannel {
161163

162164
private FileChannel fileChannel;
163165

166+
private boolean fileChannelInterrupted;
167+
168+
private RandomAccessFile randomAccessFile;
169+
164170
private ByteBuffer buffer;
165171

166172
private long bufferPosition = -1;
@@ -169,7 +175,7 @@ static class ManagedFileChannel {
169175

170176
private final Object lock = new Object();
171177

172-
ManagedFileChannel(Path path) {
178+
FileAccess(Path path) {
173179
if (!Files.isRegularFile(path)) {
174180
throw new IllegalArgumentException(path + " must be a regular file");
175181
}
@@ -193,34 +199,45 @@ int read(ByteBuffer dst, long position) throws IOException {
193199
}
194200

195201
private void fillBuffer(long position) throws IOException {
196-
for (int i = 0; i < 10; i++) {
197-
boolean interrupted = (i != 0) ? Thread.interrupted() : false;
198-
try {
199-
this.buffer.clear();
200-
this.bufferSize = this.fileChannel.read(this.buffer, position);
201-
this.bufferPosition = position;
202-
return;
203-
}
204-
catch (ClosedByInterruptException ex) {
202+
if (Thread.currentThread().isInterrupted()) {
203+
fillBufferUsingRandomAccessFile(position);
204+
return;
205+
}
206+
try {
207+
if (this.fileChannelInterrupted) {
205208
repairFileChannel();
209+
this.fileChannelInterrupted = false;
206210
}
207-
finally {
208-
if (interrupted) {
209-
Thread.currentThread().interrupt();
210-
}
211-
}
211+
this.buffer.clear();
212+
this.bufferSize = this.fileChannel.read(this.buffer, position);
213+
this.bufferPosition = position;
214+
}
215+
catch (ClosedByInterruptException ex) {
216+
this.fileChannelInterrupted = true;
217+
fillBufferUsingRandomAccessFile(position);
212218
}
213-
throw new ClosedByInterruptException();
214219
}
215220

216-
private void repairFileChannel() throws IOException {
217-
if (tracker != null) {
218-
tracker.closedFileChannel(this.path, this.fileChannel);
221+
private void fillBufferUsingRandomAccessFile(long position) throws IOException {
222+
if (this.randomAccessFile == null) {
223+
this.randomAccessFile = new RandomAccessFile(this.path.toFile(), "r");
224+
tracker.openedFileChannel(this.path);
219225
}
220-
this.fileChannel = FileChannel.open(this.path, StandardOpenOption.READ);
221-
if (tracker != null) {
222-
tracker.openedFileChannel(this.path, this.fileChannel);
226+
byte[] bytes = new byte[BUFFER_SIZE];
227+
this.randomAccessFile.seek(position);
228+
int len = this.randomAccessFile.read(bytes);
229+
this.buffer.clear();
230+
if (len > 0) {
231+
this.buffer.put(bytes, 0, len);
223232
}
233+
this.bufferSize = len;
234+
this.bufferPosition = position;
235+
}
236+
237+
private void repairFileChannel() throws IOException {
238+
tracker.closedFileChannel(this.path);
239+
this.fileChannel = FileChannel.open(this.path, StandardOpenOption.READ);
240+
tracker.openedFileChannel(this.path);
224241
}
225242

226243
void open() throws IOException {
@@ -229,9 +246,7 @@ void open() throws IOException {
229246
debug.log("Opening '%s'", this.path);
230247
this.fileChannel = FileChannel.open(this.path, StandardOpenOption.READ);
231248
this.buffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
232-
if (tracker != null) {
233-
tracker.openedFileChannel(this.path, this.fileChannel);
234-
}
249+
tracker.openedFileChannel(this.path);
235250
}
236251
this.referenceCount++;
237252
debug.log("Reference count for '%s' incremented to %s", this.path, this.referenceCount);
@@ -250,18 +265,21 @@ void close() throws IOException {
250265
this.bufferPosition = -1;
251266
this.bufferSize = 0;
252267
this.fileChannel.close();
253-
if (tracker != null) {
254-
tracker.closedFileChannel(this.path, this.fileChannel);
255-
}
268+
tracker.closedFileChannel(this.path);
256269
this.fileChannel = null;
270+
if (this.randomAccessFile != null) {
271+
this.randomAccessFile.close();
272+
tracker.closedFileChannel(this.path);
273+
this.randomAccessFile = null;
274+
}
257275
}
258276
debug.log("Reference count for '%s' decremented to %s", this.path, this.referenceCount);
259277
}
260278
}
261279

262280
<E extends Exception> void ensureOpen(Supplier<E> exceptionSupplier) throws E {
263281
synchronized (this.lock) {
264-
if (this.referenceCount == 0 || !this.fileChannel.isOpen()) {
282+
if (this.referenceCount == 0) {
265283
throw exceptionSupplier.get();
266284
}
267285
}
@@ -279,9 +297,21 @@ public String toString() {
279297
*/
280298
interface Tracker {
281299

282-
void openedFileChannel(Path path, FileChannel fileChannel);
300+
Tracker NONE = new Tracker() {
301+
302+
@Override
303+
public void openedFileChannel(Path path) {
304+
}
305+
306+
@Override
307+
public void closedFileChannel(Path path) {
308+
}
309+
310+
};
311+
312+
void openedFileChannel(Path path);
283313

284-
void closedFileChannel(Path path, FileChannel fileChannel);
314+
void closedFileChannel(Path path);
285315

286316
}
287317

spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipContent.java

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public final class ZipContent implements Closeable {
7474

7575
private final Kind kind;
7676

77-
private final FileChannelDataBlock data;
77+
private final FileDataBlock data;
7878

7979
private final long centralDirectoryPos;
8080

@@ -96,7 +96,7 @@ public final class ZipContent implements Closeable {
9696

9797
private SoftReference<Map<Class<?>, Object>> info;
9898

99-
private ZipContent(Source source, Kind kind, FileChannelDataBlock data, long centralDirectoryPos, long commentPos,
99+
private ZipContent(Source source, Kind kind, FileDataBlock data, long centralDirectoryPos, long commentPos,
100100
long commentLength, int[] lookupIndexes, int[] nameHashLookups, int[] relativeCentralDirectoryOffsetLookups,
101101
NameOffsetLookups nameOffsetLookups, boolean hasJarSignatureFile) {
102102
this.source = source;
@@ -449,7 +449,7 @@ private static final class Loader {
449449

450450
private final Source source;
451451

452-
private final FileChannelDataBlock data;
452+
private final FileDataBlock data;
453453

454454
private final long centralDirectoryPos;
455455

@@ -463,8 +463,7 @@ private static final class Loader {
463463

464464
private int cursor;
465465

466-
private Loader(Source source, Entry directoryEntry, FileChannelDataBlock data, long centralDirectoryPos,
467-
int maxSize) {
466+
private Loader(Source source, Entry directoryEntry, FileDataBlock data, long centralDirectoryPos, int maxSize) {
468467
this.source = source;
469468
this.data = data;
470469
this.centralDirectoryPos = centralDirectoryPos;
@@ -561,7 +560,7 @@ static ZipContent load(Source source) throws IOException {
561560

562561
private static ZipContent loadNonNested(Source source) throws IOException {
563562
debug.log("Loading non-nested zip '%s'", source.path());
564-
return openAndLoad(source, Kind.ZIP, new FileChannelDataBlock(source.path()));
563+
return openAndLoad(source, Kind.ZIP, new FileDataBlock(source.path()));
565564
}
566565

567566
private static ZipContent loadNestedZip(Source source, Entry entry) throws IOException {
@@ -573,7 +572,7 @@ private static ZipContent loadNestedZip(Source source, Entry entry) throws IOExc
573572
return openAndLoad(source, Kind.NESTED_ZIP, entry.getContent());
574573
}
575574

576-
private static ZipContent openAndLoad(Source source, Kind kind, FileChannelDataBlock data) throws IOException {
575+
private static ZipContent openAndLoad(Source source, Kind kind, FileDataBlock data) throws IOException {
577576
try {
578577
data.open();
579578
return loadContent(source, kind, data);
@@ -584,7 +583,7 @@ private static ZipContent openAndLoad(Source source, Kind kind, FileChannelDataB
584583
}
585584
}
586585

587-
private static ZipContent loadContent(Source source, Kind kind, FileChannelDataBlock data) throws IOException {
586+
private static ZipContent loadContent(Source source, Kind kind, FileDataBlock data) throws IOException {
588587
ZipEndOfCentralDirectoryRecord.Located locatedEocd = ZipEndOfCentralDirectoryRecord.load(data);
589588
ZipEndOfCentralDirectoryRecord eocd = locatedEocd.endOfCentralDirectoryRecord();
590589
long eocdPos = locatedEocd.pos();
@@ -634,7 +633,7 @@ private static ZipContent loadContent(Source source, Kind kind, FileChannelDataB
634633
* @return the offset within the data where the archive begins
635634
* @throws IOException on I/O error
636635
*/
637-
private static long getStartOfZipContent(FileChannelDataBlock data, ZipEndOfCentralDirectoryRecord eocd,
636+
private static long getStartOfZipContent(FileDataBlock data, ZipEndOfCentralDirectoryRecord eocd,
638637
Zip64EndOfCentralDirectoryRecord zip64Eocd) throws IOException {
639638
long specifiedOffsetToStartOfCentralDirectory = (zip64Eocd != null)
640639
? zip64Eocd.offsetToStartOfCentralDirectory() : eocd.offsetToStartOfCentralDirectory();
@@ -699,7 +698,7 @@ public class Entry {
699698

700699
private volatile String name;
701700

702-
private volatile FileChannelDataBlock content;
701+
private volatile FileDataBlock content;
703702

704703
/**
705704
* Create a new {@link Entry} instance.
@@ -789,13 +788,13 @@ public int getUncompressedSize() {
789788
* @throws IOException on I/O error
790789
*/
791790
public CloseableDataBlock openContent() throws IOException {
792-
FileChannelDataBlock content = getContent();
791+
FileDataBlock content = getContent();
793792
content.open();
794793
return content;
795794
}
796795

797-
private FileChannelDataBlock getContent() throws IOException {
798-
FileChannelDataBlock content = this.content;
796+
private FileDataBlock getContent() throws IOException {
797+
FileDataBlock content = this.content;
799798
if (content == null) {
800799
int pos = this.centralRecord.offsetToLocalHeader();
801800
checkNotZip64Extended(pos);

0 commit comments

Comments
 (0)