Skip to content

Commit ff50fba

Browse files
committed
minified refactoring by not breaking package boundaries
1 parent e432b1e commit ff50fba

File tree

6 files changed

+53
-34
lines changed

6 files changed

+53
-34
lines changed

src/main/java/org/cryptomator/cryptofs/ch/ChannelComponent.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.cryptomator.cryptofs.EffectiveOpenOptions;
66

77
import java.nio.channels.FileChannel;
8+
import java.util.function.Consumer;
89

910
@ChannelScoped
1011
@Subcomponent
@@ -17,7 +18,7 @@ interface Factory {
1718

1819
ChannelComponent create(@BindsInstance FileChannel ciphertextChannel, //
1920
@BindsInstance EffectiveOpenOptions options, //
20-
@BindsInstance Runnable closeListener); //
21+
@BindsInstance Consumer<FileChannel> closeListener); //
2122
}
2223

2324
}

src/main/java/org/cryptomator/cryptofs/ch/CleartextFileChannel.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import org.cryptomator.cryptofs.fh.BufferPool;
88
import org.cryptomator.cryptofs.fh.Chunk;
99
import org.cryptomator.cryptofs.fh.ChunkCache;
10-
import org.cryptomator.cryptofs.fh.ChunkIO;
1110
import org.cryptomator.cryptofs.fh.CurrentOpenFilePath;
1211
import org.cryptomator.cryptofs.fh.ExceptionsDuringWrite;
1312
import org.cryptomator.cryptofs.fh.FileHeaderHolder;
@@ -35,6 +34,7 @@
3534
import java.util.concurrent.atomic.AtomicLong;
3635
import java.util.concurrent.atomic.AtomicReference;
3736
import java.util.concurrent.locks.ReadWriteLock;
37+
import java.util.function.Consumer;
3838

3939
import static java.lang.Math.max;
4040
import static java.lang.Math.min;
@@ -47,24 +47,22 @@ public class CleartextFileChannel extends AbstractFileChannel {
4747
private final FileChannel ciphertextFileChannel;
4848
private final FileHeaderHolder fileHeaderHolder;
4949
private final Cryptor cryptor;
50-
private final ChunkIO chunkIO;
5150
private final ChunkCache chunkCache;
5251
private final BufferPool bufferPool;
5352
private final EffectiveOpenOptions options;
5453
private final AtomicReference<Path> currentFilePath;
5554
private final AtomicLong fileSize;
5655
private final AtomicReference<Instant> lastModified;
5756
private final ExceptionsDuringWrite exceptionsDuringWrite;
58-
private final Runnable closeListener;
57+
private final Consumer<FileChannel> closeListener;
5958
private final CryptoFileSystemStats stats;
6059

6160
@Inject
62-
public CleartextFileChannel(FileChannel ciphertextFileChannel, FileHeaderHolder fileHeaderHolder, ReadWriteLock readWriteLock, Cryptor cryptor, ChunkIO chunkIO, ChunkCache chunkCache, BufferPool bufferPool, EffectiveOpenOptions options, @OpenFileSize AtomicLong fileSize, @OpenFileModifiedDate AtomicReference<Instant> lastModified, @CurrentOpenFilePath AtomicReference<Path> currentPath, ExceptionsDuringWrite exceptionsDuringWrite, Runnable closeListener, CryptoFileSystemStats stats) {
61+
public CleartextFileChannel(FileChannel ciphertextFileChannel, FileHeaderHolder fileHeaderHolder, ReadWriteLock readWriteLock, Cryptor cryptor, ChunkCache chunkCache, BufferPool bufferPool, EffectiveOpenOptions options, @OpenFileSize AtomicLong fileSize, @OpenFileModifiedDate AtomicReference<Instant> lastModified, @CurrentOpenFilePath AtomicReference<Path> currentPath, ExceptionsDuringWrite exceptionsDuringWrite, Consumer<FileChannel> closeListener, CryptoFileSystemStats stats) {
6362
super(readWriteLock);
6463
this.ciphertextFileChannel = ciphertextFileChannel;
6564
this.fileHeaderHolder = fileHeaderHolder;
6665
this.cryptor = cryptor;
67-
this.chunkIO = chunkIO;
6866
this.chunkCache = chunkCache;
6967
this.bufferPool = bufferPool;
7068
this.options = options;
@@ -80,7 +78,6 @@ public CleartextFileChannel(FileChannel ciphertextFileChannel, FileHeaderHolder
8078
if (options.createNew() || options.create()) {
8179
lastModified.compareAndSet(Instant.EPOCH, Instant.now());
8280
}
83-
chunkIO.registerChannel(ciphertextFileChannel, options.writable());
8481
}
8582

8683
@Override
@@ -331,8 +328,7 @@ long beginOfChunk(long cleartextPos) {
331328
protected void implCloseChannel() throws IOException {
332329
var closeActions = List.<CloseAction>of(this::flush, //
333330
super::implCloseChannel, //
334-
() -> chunkIO.unregisterChannel(ciphertextFileChannel), // _after_ flush, since the flushed chunk cache uses chunkIO file channels
335-
closeListener::run, //
331+
() -> closeListener.accept(ciphertextFileChannel),
336332
ciphertextFileChannel::close, //
337333
this::tryPersistLastModified);
338334
tryAll(closeActions.iterator());

src/main/java/org/cryptomator/cryptofs/fh/ChunkIO.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import java.util.concurrent.ConcurrentHashMap;
1212

1313
@OpenFileScoped
14-
public class ChunkIO {
14+
class ChunkIO {
1515

1616
private final Set<FileChannel> readableChannels = ConcurrentHashMap.newKeySet();
1717
private final Set<FileChannel> writableChannels = ConcurrentHashMap.newKeySet();

src/main/java/org/cryptomator/cryptofs/fh/OpenCryptoFile.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,22 @@ public class OpenCryptoFile implements Closeable {
2828
private final ChunkCache chunkCache;
2929
private final Cryptor cryptor;
3030
private final FileHeaderHolder headerHolder;
31+
private final ChunkIO chunkIO;
3132
private final AtomicReference<Path> currentFilePath;
3233
private final AtomicLong fileSize;
3334
private final OpenCryptoFileComponent component;
3435

3536
private volatile int openChannelsCount = 0;
3637

3738
@Inject
38-
public OpenCryptoFile(FileCloseListener listener, ChunkCache chunkCache, Cryptor cryptor, FileHeaderHolder headerHolder, @CurrentOpenFilePath AtomicReference<Path> currentFilePath, @OpenFileSize AtomicLong fileSize, @OpenFileModifiedDate AtomicReference<Instant> lastModified, OpenCryptoFileComponent component) {
39+
public OpenCryptoFile(FileCloseListener listener, ChunkCache chunkCache, Cryptor cryptor, FileHeaderHolder headerHolder, ChunkIO chunkIO, //
40+
@CurrentOpenFilePath AtomicReference<Path> currentFilePath, @OpenFileSize AtomicLong fileSize,
41+
@OpenFileModifiedDate AtomicReference<Instant> lastModified, OpenCryptoFileComponent component) {
3942
this.listener = listener;
4043
this.chunkCache = chunkCache;
4144
this.cryptor = cryptor;
4245
this.headerHolder = headerHolder;
46+
this.chunkIO = chunkIO;
4347
this.currentFilePath = currentFilePath;
4448
this.fileSize = fileSize;
4549
this.component = component;
@@ -72,12 +76,13 @@ public synchronized FileChannel newFileChannel(EffectiveOpenOptions options, Fil
7276
}
7377
initFileSize(ciphertextFileChannel);
7478
cleartextFileChannel = component.newChannelComponent() //
75-
.create(ciphertextFileChannel, options, this::channelClosed) //
79+
.create(ciphertextFileChannel, options, this::cleartextChannelClosed) //
7680
.channel();
81+
chunkIO.registerChannel(ciphertextFileChannel,options.writable());
7782
} finally {
7883
if (cleartextFileChannel == null) { // i.e. something didn't work
84+
cleartextChannelClosed(ciphertextFileChannel);
7985
closeQuietly(ciphertextFileChannel);
80-
channelClosed(); // if first channel and it fails, close this again
8186
}
8287
}
8388

@@ -169,7 +174,11 @@ public void updateCurrentFilePath(Path newFilePath) {
169174
currentFilePath.updateAndGet(p -> p == null ? null : newFilePath);
170175
}
171176

172-
private synchronized void channelClosed() {
177+
private synchronized void cleartextChannelClosed(FileChannel ciphertextFileChannel) {
178+
if( ciphertextFileChannel != null ){
179+
chunkIO.unregisterChannel(ciphertextFileChannel);
180+
}
181+
173182
openChannelsCount = openChannelsCount - 1;
174183
if (openChannelsCount == 0) {
175184
close();

src/test/java/org/cryptomator/cryptofs/ch/CleartextFileChannelTest.java

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import org.cryptomator.cryptofs.fh.BufferPool;
66
import org.cryptomator.cryptofs.fh.Chunk;
77
import org.cryptomator.cryptofs.fh.ChunkCache;
8-
import org.cryptomator.cryptofs.fh.ChunkIO;
98
import org.cryptomator.cryptofs.fh.ExceptionsDuringWrite;
109
import org.cryptomator.cryptofs.fh.FileHeaderHolder;
1110
import org.cryptomator.cryptolib.api.Cryptor;
@@ -44,6 +43,7 @@
4443
import java.util.concurrent.atomic.AtomicReference;
4544
import java.util.concurrent.locks.Lock;
4645
import java.util.concurrent.locks.ReadWriteLock;
46+
import java.util.function.Consumer;
4747

4848
import static org.hamcrest.CoreMatchers.is;
4949
import static org.mockito.ArgumentMatchers.any;
@@ -60,7 +60,6 @@
6060
public class CleartextFileChannelTest {
6161

6262
private ChunkCache chunkCache = mock(ChunkCache.class);
63-
private ChunkIO chunkIO = mock(ChunkIO.class);
6463
private BufferPool bufferPool = mock(BufferPool.class);
6564
private ReadWriteLock readWriteLock = mock(ReadWriteLock.class);
6665
private Lock readLock = mock(Lock.class);
@@ -78,7 +77,7 @@ public class CleartextFileChannelTest {
7877
private AtomicReference<Instant> lastModified = new AtomicReference<>(Instant.ofEpochMilli(0));
7978
private BasicFileAttributeView attributeView = mock(BasicFileAttributeView.class);
8079
private ExceptionsDuringWrite exceptionsDuringWrite = mock(ExceptionsDuringWrite.class);
81-
private Runnable closeListener = mock(Runnable.class);
80+
private Consumer<FileChannel> closeListener = mock(Consumer.class);
8281
private CryptoFileSystemStats stats = mock(CryptoFileSystemStats.class);
8382

8483
private CleartextFileChannel inTest;
@@ -89,8 +88,6 @@ public void setUp() throws IOException {
8988
when(cryptor.fileContentCryptor()).thenReturn(fileContentCryptor);
9089
when(chunkCache.getChunk(Mockito.anyLong())).then(invocation -> new Chunk(ByteBuffer.allocate(100), false, () -> {}));
9190
when(chunkCache.putChunk(Mockito.anyLong(), Mockito.any())).thenAnswer(invocation -> new Chunk(invocation.getArgument(1), true, () -> {}));
92-
doNothing().when(chunkIO).registerChannel(Mockito.eq(ciphertextFileChannel), Mockito.anyBoolean());
93-
doNothing().when(chunkIO).unregisterChannel(ciphertextFileChannel);
9491
when(bufferPool.getCleartextBuffer()).thenAnswer(invocation -> ByteBuffer.allocate(100));
9592
when(fileHeaderCryptor.headerSize()).thenReturn(50);
9693
when(headerHolder.headerIsPersisted()).thenReturn(headerIsPersisted);
@@ -105,7 +102,7 @@ public void setUp() throws IOException {
105102
when(readWriteLock.readLock()).thenReturn(readLock);
106103
when(readWriteLock.writeLock()).thenReturn(writeLock);
107104

108-
inTest = new CleartextFileChannel(ciphertextFileChannel, headerHolder, readWriteLock, cryptor, chunkIO, chunkCache, bufferPool, options, fileSize, lastModified, currentFilePath, exceptionsDuringWrite, closeListener, stats);
105+
inTest = new CleartextFileChannel(ciphertextFileChannel, headerHolder, readWriteLock, cryptor, chunkCache, bufferPool, options, fileSize, lastModified, currentFilePath, exceptionsDuringWrite, closeListener, stats);
109106
}
110107

111108
@Test
@@ -246,9 +243,8 @@ public void testCloseIoExceptionFlush() throws IOException {
246243

247244
Assertions.assertThrows(IOException.class, () -> inSpy.implCloseChannel());
248245

249-
verify(closeListener).run();
246+
verify(closeListener).accept(ciphertextFileChannel);
250247
verify(ciphertextFileChannel).close();
251-
verify(chunkIO).unregisterChannel(ciphertextFileChannel);
252248
verify(inSpy).persistLastModified();
253249
}
254250

@@ -258,9 +254,9 @@ public void testCloseCipherChannelFlushBeforeUnregister() throws IOException {
258254
var inSpy = spy(inTest);
259255
inSpy.implCloseChannel();
260256

261-
var ordering = inOrder(inSpy, chunkIO);
257+
var ordering = inOrder(inSpy, closeListener);
262258
ordering.verify(inSpy).flush();
263-
ordering.verify(chunkIO).unregisterChannel(ciphertextFileChannel);
259+
verify(closeListener).accept(ciphertextFileChannel);
264260
}
265261

266262
@Test
@@ -294,9 +290,8 @@ public void testCloseExceptionOnLastModifiedPersistenceIgnored() throws IOExcept
294290
var inSpy = Mockito.spy(inTest);
295291
Mockito.doThrow(IOException.class).when(inSpy).persistLastModified();
296292

297-
Assertions.assertDoesNotThrow(() -> inSpy.implCloseChannel());
298-
verify(chunkIO).unregisterChannel(ciphertextFileChannel);
299-
verify(closeListener).run();
293+
Assertions.assertDoesNotThrow(inSpy::implCloseChannel);
294+
verify(closeListener).accept(ciphertextFileChannel);
300295
verify(ciphertextFileChannel).close();
301296
}
302297

@@ -402,7 +397,7 @@ public void testReadFromMultipleChunks() throws IOException {
402397
fileSize.set(5_000_000_100l); // initial cleartext size will be 5_000_000_100l
403398
when(options.readable()).thenReturn(true);
404399

405-
inTest = new CleartextFileChannel(ciphertextFileChannel, headerHolder, readWriteLock, cryptor, chunkIO, chunkCache, bufferPool, options, fileSize, lastModified, currentFilePath, exceptionsDuringWrite, closeListener, stats);
400+
inTest = new CleartextFileChannel(ciphertextFileChannel, headerHolder, readWriteLock, cryptor, chunkCache, bufferPool, options, fileSize, lastModified, currentFilePath, exceptionsDuringWrite, closeListener, stats);
406401
ByteBuffer buf = ByteBuffer.allocate(10);
407402

408403
// A read from frist chunk:
@@ -574,7 +569,7 @@ public void testWriteHeaderFailsResetsPersistenceState() throws IOException {
574569
public void testDontRewriteHeader() throws IOException {
575570
when(options.writable()).thenReturn(true);
576571
when(headerIsPersisted.get()).thenReturn(true);
577-
inTest = new CleartextFileChannel(ciphertextFileChannel, headerHolder, readWriteLock, cryptor, chunkIO, chunkCache, bufferPool, options, fileSize, lastModified, currentFilePath, exceptionsDuringWrite, closeListener, stats);
572+
inTest = new CleartextFileChannel(ciphertextFileChannel, headerHolder, readWriteLock, cryptor, chunkCache, bufferPool, options, fileSize, lastModified, currentFilePath, exceptionsDuringWrite, closeListener, stats);
578573

579574
inTest.force(true);
580575

src/test/java/org/cryptomator/cryptofs/fh/OpenCryptoFileTest.java

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.cryptomator.cryptolib.api.FileHeaderCryptor;
1212
import org.junit.jupiter.api.AfterAll;
1313
import org.junit.jupiter.api.Assertions;
14+
import org.junit.jupiter.api.Assumptions;
1415
import org.junit.jupiter.api.BeforeAll;
1516
import org.junit.jupiter.api.DisplayName;
1617
import org.junit.jupiter.api.MethodOrderer;
@@ -33,6 +34,7 @@
3334
import java.util.EnumSet;
3435
import java.util.concurrent.atomic.AtomicLong;
3536
import java.util.concurrent.atomic.AtomicReference;
37+
import java.util.function.Consumer;
3638

3739
import static org.mockito.ArgumentMatchers.any;
3840
import static org.mockito.Mockito.mock;
@@ -50,6 +52,7 @@ public class OpenCryptoFileTest {
5052
private Cryptor cryptor = mock(Cryptor.class);
5153
private FileHeaderCryptor fileHeaderCryptor = mock(FileHeaderCryptor.class);
5254
private FileHeaderHolder headerHolder = mock(FileHeaderHolder.class);
55+
private ChunkIO chunkIO = mock(ChunkIO.class);
5356
private AtomicLong fileSize = Mockito.mock(AtomicLong.class);
5457
private AtomicReference<Instant> lastModified = new AtomicReference(Instant.ofEpochMilli(0));
5558
private OpenCryptoFileComponent openCryptoFileComponent = mock(OpenCryptoFileComponent.class);
@@ -69,7 +72,7 @@ public static void tearDown() throws IOException {
6972

7073
@Test
7174
public void testCloseTriggersCloseListener() {
72-
OpenCryptoFile openCryptoFile = new OpenCryptoFile(closeListener, chunkCache, cryptor, headerHolder, CURRENT_FILE_PATH, fileSize, lastModified, openCryptoFileComponent);
75+
OpenCryptoFile openCryptoFile = new OpenCryptoFile(closeListener, chunkCache, cryptor, headerHolder, chunkIO, CURRENT_FILE_PATH, fileSize, lastModified, openCryptoFileComponent);
7376
openCryptoFile.close();
7477
verify(closeListener).close(CURRENT_FILE_PATH.get(), openCryptoFile);
7578
}
@@ -80,7 +83,7 @@ public void testCloseImmediatelyIfOpeningFirstChannelFails() {
8083
UncheckedIOException expectedException = new UncheckedIOException(new IOException("fail!"));
8184
EffectiveOpenOptions options = Mockito.mock(EffectiveOpenOptions.class);
8285
Mockito.when(options.createOpenOptionsForEncryptedFile()).thenThrow(expectedException);
83-
OpenCryptoFile openCryptoFile = new OpenCryptoFile(closeListener, chunkCache, cryptor, headerHolder, CURRENT_FILE_PATH, fileSize, lastModified, openCryptoFileComponent);
86+
OpenCryptoFile openCryptoFile = new OpenCryptoFile(closeListener, chunkCache, cryptor, headerHolder, chunkIO, CURRENT_FILE_PATH, fileSize, lastModified, openCryptoFileComponent);
8487

8588
UncheckedIOException exception = Assertions.assertThrows(UncheckedIOException.class, () -> {
8689
openCryptoFile.newFileChannel(options);
@@ -99,7 +102,7 @@ public void testFileSizeZerodOnTruncateExisting() throws IOException {
99102
Mockito.when(openCryptoFileComponent.newChannelComponent()).thenReturn(channelComponentFactory);
100103
Mockito.when(channelComponentFactory.create(any(), any(), any())).thenReturn(channelComponent);
101104
Mockito.when(channelComponent.channel()).thenReturn(mock(CleartextFileChannel.class));
102-
OpenCryptoFile openCryptoFile = new OpenCryptoFile(closeListener, chunkCache, cryptor, headerHolder, CURRENT_FILE_PATH, fileSize, lastModified, openCryptoFileComponent);
105+
OpenCryptoFile openCryptoFile = new OpenCryptoFile(closeListener, chunkCache, cryptor, headerHolder, chunkIO, CURRENT_FILE_PATH, fileSize, lastModified, openCryptoFileComponent);
103106

104107
openCryptoFile.newFileChannel(options);
105108
verify(fileSize).set(0L);
@@ -111,7 +114,7 @@ public class InitFilHeaderTests {
111114

112115
EffectiveOpenOptions options = Mockito.mock(EffectiveOpenOptions.class);
113116
FileChannel cipherFileChannel = Mockito.mock(FileChannel.class, "cipherFilechannel");
114-
OpenCryptoFile inTest = new OpenCryptoFile(closeListener, chunkCache, cryptor, headerHolder, CURRENT_FILE_PATH, fileSize, lastModified, openCryptoFileComponent);
117+
OpenCryptoFile inTest = new OpenCryptoFile(closeListener, chunkCache, cryptor, headerHolder, chunkIO, CURRENT_FILE_PATH, fileSize, lastModified, openCryptoFileComponent);
115118

116119
@Test
117120
@DisplayName("Skip file header init, if the file header already exists in memory")
@@ -188,19 +191,22 @@ public class FileChannelFactoryTest {
188191
private final AtomicLong realFileSize = new AtomicLong(-1L);
189192
private OpenCryptoFile openCryptoFile;
190193
private CleartextFileChannel cleartextFileChannel;
194+
private AtomicReference<Consumer<FileChannel>> listener;
191195
private AtomicReference<FileChannel> ciphertextChannel;
192196

193197
@BeforeAll
194198
public void setup() throws IOException {
195199
FS = Jimfs.newFileSystem("OpenCryptoFileTest.FileChannelFactoryTest", Configuration.unix().toBuilder().setAttributeViews("basic", "posix").build());
196200
CURRENT_FILE_PATH = new AtomicReference<>(FS.getPath("currentFile"));
197-
openCryptoFile = new OpenCryptoFile(closeListener, chunkCache, cryptor, headerHolder, CURRENT_FILE_PATH, realFileSize, lastModified, openCryptoFileComponent);
201+
openCryptoFile = new OpenCryptoFile(closeListener, chunkCache, cryptor, headerHolder, chunkIO, CURRENT_FILE_PATH, realFileSize, lastModified, openCryptoFileComponent);
198202
cleartextFileChannel = mock(CleartextFileChannel.class);
203+
listener = new AtomicReference<>();
199204
ciphertextChannel = new AtomicReference<>();
200205

201206
Mockito.when(openCryptoFileComponent.newChannelComponent()).thenReturn(channelComponentFactory);
202207
Mockito.when(channelComponentFactory.create(Mockito.any(), Mockito.any(), Mockito.any())).thenAnswer(invocation -> {
203208
ciphertextChannel.set(invocation.getArgument(0));
209+
listener.set(invocation.getArgument(2));
204210
return channelComponent;
205211
});
206212
Mockito.when(channelComponent.channel()).thenReturn(cleartextFileChannel);
@@ -221,6 +227,7 @@ public void createFileChannel() throws IOException {
221227
EffectiveOpenOptions options = EffectiveOpenOptions.from(EnumSet.of(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE), readonlyFlag);
222228
FileChannel ch = openCryptoFile.newFileChannel(options, attrs);
223229
Assertions.assertSame(cleartextFileChannel, ch);
230+
verify(chunkIO).registerChannel(ciphertextChannel.get(), true);
224231
}
225232

226233
@Test
@@ -266,6 +273,17 @@ public void testTruncateExistingInvalidatesChunkCache() throws IOException {
266273
verify(chunkCache).invalidateStale();
267274
}
268275

276+
@Test
277+
@Order(100)
278+
@DisplayName("closeListener triggers chunkIO.unregisterChannel()")
279+
public void triggerCloseListener() throws IOException {
280+
Assumptions.assumeTrue(listener.get() != null);
281+
Assumptions.assumeTrue(ciphertextChannel.get() != null);
282+
283+
listener.get().accept(ciphertextChannel.get());
284+
verify(chunkIO).unregisterChannel(ciphertextChannel.get());
285+
}
286+
269287
}
270288

271289
}

0 commit comments

Comments
 (0)