Skip to content

Commit 4806744

Browse files
authored
Merge pull request #238 from cryptomator/feature/inversion-of-chunkregister
Refactoring: Simplify OpenCryptoFile
2 parents d0943c1 + eefc448 commit 4806744

File tree

6 files changed

+37
-41
lines changed

6 files changed

+37
-41
lines changed

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

Lines changed: 0 additions & 9 deletions
This file was deleted.

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 ChannelCloseListener listener); //
21+
@BindsInstance Consumer<FileChannel> closeListener); //
2122
}
2223

2324
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import java.util.concurrent.atomic.AtomicLong;
3535
import java.util.concurrent.atomic.AtomicReference;
3636
import java.util.concurrent.locks.ReadWriteLock;
37+
import java.util.function.Consumer;
3738

3839
import static java.lang.Math.max;
3940
import static java.lang.Math.min;
@@ -53,11 +54,11 @@ public class CleartextFileChannel extends AbstractFileChannel {
5354
private final AtomicLong fileSize;
5455
private final AtomicReference<Instant> lastModified;
5556
private final ExceptionsDuringWrite exceptionsDuringWrite;
56-
private final ChannelCloseListener closeListener;
57+
private final Consumer<FileChannel> closeListener;
5758
private final CryptoFileSystemStats stats;
5859

5960
@Inject
60-
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, ChannelCloseListener 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) {
6162
super(readWriteLock);
6263
this.ciphertextFileChannel = ciphertextFileChannel;
6364
this.fileHeaderHolder = fileHeaderHolder;
@@ -327,7 +328,7 @@ long beginOfChunk(long cleartextPos) {
327328
protected void implCloseChannel() throws IOException {
328329
var closeActions = List.<CloseAction>of(this::flush, //
329330
super::implCloseChannel, //
330-
() -> closeListener.closed(this), //
331+
() -> closeListener.accept(ciphertextFileChannel),
331332
ciphertextFileChannel::close, //
332333
this::tryPersistLastModified);
333334
tryAll(closeActions.iterator());

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

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
1-
/*******************************************************************************
2-
* Copyright (c) 2016 Sebastian Stenzel and others.
3-
* All rights reserved. This program and the accompanying materials
4-
* are made available under the terms of the accompanying LICENSE.txt.
5-
*
6-
* Contributors:
7-
* Sebastian Stenzel - initial API and implementation
8-
*******************************************************************************/
91
package org.cryptomator.cryptofs.fh;
102

113
import org.cryptomator.cryptofs.EffectiveOpenOptions;
@@ -23,8 +15,7 @@
2315
import java.nio.file.attribute.FileTime;
2416
import java.time.Instant;
2517
import java.util.Optional;
26-
import java.util.concurrent.ConcurrentHashMap;
27-
import java.util.concurrent.ConcurrentMap;
18+
import java.util.concurrent.atomic.AtomicInteger;
2819
import java.util.concurrent.atomic.AtomicLong;
2920
import java.util.concurrent.atomic.AtomicReference;
3021

@@ -42,10 +33,13 @@ public class OpenCryptoFile implements Closeable {
4233
private final AtomicReference<Path> currentFilePath;
4334
private final AtomicLong fileSize;
4435
private final OpenCryptoFileComponent component;
45-
private final ConcurrentMap<CleartextFileChannel, FileChannel> openChannels = new ConcurrentHashMap<>();
36+
37+
private final AtomicInteger openChannelsCount = new AtomicInteger(0);
4638

4739
@Inject
48-
public OpenCryptoFile(FileCloseListener listener, ChunkCache chunkCache, Cryptor cryptor, FileHeaderHolder headerHolder, ChunkIO chunkIO, @CurrentOpenFilePath AtomicReference<Path> currentFilePath, @OpenFileSize AtomicLong fileSize, @OpenFileModifiedDate AtomicReference<Instant> lastModified, OpenCryptoFileComponent component) {
40+
public OpenCryptoFile(FileCloseListener listener, ChunkCache chunkCache, Cryptor cryptor, FileHeaderHolder headerHolder, ChunkIO chunkIO, //
41+
@CurrentOpenFilePath AtomicReference<Path> currentFilePath, @OpenFileSize AtomicLong fileSize, //
42+
@OpenFileModifiedDate AtomicReference<Instant> lastModified, OpenCryptoFileComponent component) {
4943
this.listener = listener;
5044
this.chunkCache = chunkCache;
5145
this.cryptor = cryptor;
@@ -71,6 +65,8 @@ public synchronized FileChannel newFileChannel(EffectiveOpenOptions options, Fil
7165
}
7266
FileChannel ciphertextFileChannel = null;
7367
CleartextFileChannel cleartextFileChannel = null;
68+
69+
openChannelsCount.incrementAndGet(); // synchronized context, hence we can proactively increase the number
7470
try {
7571
ciphertextFileChannel = path.getFileSystem().provider().newFileChannel(path, options.createOpenOptionsForEncryptedFile(), attrs);
7672
initFileHeader(options, ciphertextFileChannel);
@@ -81,20 +77,16 @@ public synchronized FileChannel newFileChannel(EffectiveOpenOptions options, Fil
8177
}
8278
initFileSize(ciphertextFileChannel);
8379
cleartextFileChannel = component.newChannelComponent() //
84-
.create(ciphertextFileChannel, options, this::channelClosed) //
80+
.create(ciphertextFileChannel, options, this::cleartextChannelClosed) //
8581
.channel();
8682
} finally {
8783
if (cleartextFileChannel == null) { // i.e. something didn't work
84+
cleartextChannelClosed(ciphertextFileChannel);
8885
closeQuietly(ciphertextFileChannel);
89-
// is this the first file channel to be opened?
90-
if (openChannels.isEmpty()) {
91-
close(); // then also close the file again.
92-
}
9386
}
9487
}
9588

9689
assert cleartextFileChannel != null; // otherwise there would have been an exception
97-
openChannels.put(cleartextFileChannel, ciphertextFileChannel);
9890
chunkIO.registerChannel(ciphertextFileChannel, options.writable());
9991
return cleartextFileChannel;
10092
}
@@ -183,12 +175,11 @@ public void updateCurrentFilePath(Path newFilePath) {
183175
currentFilePath.updateAndGet(p -> p == null ? null : newFilePath);
184176
}
185177

186-
private synchronized void channelClosed(CleartextFileChannel cleartextFileChannel) {
187-
FileChannel ciphertextFileChannel = openChannels.remove(cleartextFileChannel);
178+
private synchronized void cleartextChannelClosed(FileChannel ciphertextFileChannel) {
188179
if (ciphertextFileChannel != null) {
189180
chunkIO.unregisterChannel(ciphertextFileChannel);
190181
}
191-
if (openChannels.isEmpty()) {
182+
if (openChannelsCount.decrementAndGet() == 0) {
192183
close();
193184
}
194185
}

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import java.util.concurrent.atomic.AtomicReference;
4444
import java.util.concurrent.locks.Lock;
4545
import java.util.concurrent.locks.ReadWriteLock;
46+
import java.util.function.Consumer;
4647

4748
import static org.hamcrest.CoreMatchers.is;
4849
import static org.mockito.ArgumentMatchers.any;
@@ -76,7 +77,7 @@ public class CleartextFileChannelTest {
7677
private AtomicReference<Instant> lastModified = new AtomicReference<>(Instant.ofEpochMilli(0));
7778
private BasicFileAttributeView attributeView = mock(BasicFileAttributeView.class);
7879
private ExceptionsDuringWrite exceptionsDuringWrite = mock(ExceptionsDuringWrite.class);
79-
private ChannelCloseListener closeListener = mock(ChannelCloseListener.class);
80+
private Consumer<FileChannel> closeListener = mock(Consumer.class);
8081
private CryptoFileSystemStats stats = mock(CryptoFileSystemStats.class);
8182

8283
private CleartextFileChannel inTest;
@@ -242,11 +243,22 @@ public void testCloseIoExceptionFlush() throws IOException {
242243

243244
Assertions.assertThrows(IOException.class, () -> inSpy.implCloseChannel());
244245

245-
verify(closeListener).closed(inSpy);
246+
verify(closeListener).accept(ciphertextFileChannel);
246247
verify(ciphertextFileChannel).close();
247248
verify(inSpy).persistLastModified();
248249
}
249250

251+
@Test
252+
@DisplayName("On close, first flush channel, then unregister")
253+
public void testCloseCipherChannelFlushBeforeUnregister() throws IOException {
254+
var inSpy = spy(inTest);
255+
inSpy.implCloseChannel();
256+
257+
var ordering = inOrder(inSpy, closeListener);
258+
ordering.verify(inSpy).flush();
259+
verify(closeListener).accept(ciphertextFileChannel);
260+
}
261+
250262
@Test
251263
@DisplayName("On close, first close channel, then persist lastModified")
252264
public void testCloseCipherChannelCloseBeforePersist() throws IOException {
@@ -278,8 +290,8 @@ public void testCloseExceptionOnLastModifiedPersistenceIgnored() throws IOExcept
278290
var inSpy = Mockito.spy(inTest);
279291
Mockito.doThrow(IOException.class).when(inSpy).persistLastModified();
280292

281-
Assertions.assertDoesNotThrow(() -> inSpy.implCloseChannel());
282-
verify(closeListener).closed(inSpy);
293+
Assertions.assertDoesNotThrow(inSpy::implCloseChannel);
294+
verify(closeListener).accept(ciphertextFileChannel);
283295
verify(ciphertextFileChannel).close();
284296
}
285297

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import com.google.common.jimfs.Jimfs;
55
import org.cryptomator.cryptofs.EffectiveOpenOptions;
66
import org.cryptomator.cryptofs.ReadonlyFlag;
7-
import org.cryptomator.cryptofs.ch.ChannelCloseListener;
87
import org.cryptomator.cryptofs.ch.ChannelComponent;
98
import org.cryptomator.cryptofs.ch.CleartextFileChannel;
109
import org.cryptomator.cryptolib.api.Cryptor;
@@ -35,6 +34,7 @@
3534
import java.util.EnumSet;
3635
import java.util.concurrent.atomic.AtomicLong;
3736
import java.util.concurrent.atomic.AtomicReference;
37+
import java.util.function.Consumer;
3838

3939
import static org.mockito.ArgumentMatchers.any;
4040
import static org.mockito.Mockito.mock;
@@ -191,7 +191,7 @@ public class FileChannelFactoryTest {
191191
private final AtomicLong realFileSize = new AtomicLong(-1L);
192192
private OpenCryptoFile openCryptoFile;
193193
private CleartextFileChannel cleartextFileChannel;
194-
private AtomicReference<ChannelCloseListener> listener;
194+
private AtomicReference<Consumer<FileChannel>> listener;
195195
private AtomicReference<FileChannel> ciphertextChannel;
196196

197197
@BeforeAll
@@ -280,7 +280,7 @@ public void triggerCloseListener() throws IOException {
280280
Assumptions.assumeTrue(listener.get() != null);
281281
Assumptions.assumeTrue(ciphertextChannel.get() != null);
282282

283-
listener.get().closed(cleartextFileChannel);
283+
listener.get().accept(ciphertextChannel.get());
284284
verify(chunkIO).unregisterChannel(ciphertextChannel.get());
285285
}
286286

0 commit comments

Comments
 (0)