Skip to content
This repository was archived by the owner on Mar 14, 2025. It is now read-only.

Commit a275f74

Browse files
committed
Separate delete() to deleteFolder(...) and deleteFile(...)
1 parent 32046dd commit a275f74

File tree

10 files changed

+99
-52
lines changed

10 files changed

+99
-52
lines changed

src/main/java/org/cryptomator/cloudaccess/MetadataCachingProviderDecorator.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,19 @@ public CompletionStage<CloudPath> createFolder(CloudPath folder) {
133133
}
134134

135135
@Override
136-
public CompletionStage<Void> delete(CloudPath node) {
137-
return delegate.delete(node) //
136+
public CompletionStage<Void> deleteFile(CloudPath file) {
137+
return delegate.deleteFile(file) //
138138
.whenComplete((nullReturn, exception) -> {
139-
evictIncludingDescendants(node);
139+
itemMetadataCache.invalidate(file);
140+
quotaCache.invalidateAll();
141+
});
142+
}
143+
144+
@Override
145+
public CompletionStage<Void> deleteFolder(CloudPath folder) {
146+
return delegate.deleteFolder(folder) //
147+
.whenComplete((nullReturn, exception) -> {
148+
evictIncludingDescendants(folder);
140149
quotaCache.invalidateAll();
141150
});
142151
}

src/main/java/org/cryptomator/cloudaccess/api/CloudProvider.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,18 +181,32 @@ default CompletionStage<CloudPath> createFolderIfNonExisting(CloudPath folder) {
181181
}
182182

183183
/**
184-
* Recursively delete a file or folder.
184+
* Delete a file.
185185
* <p>
186186
* The returned CompletionStage might complete exceptionally with one of the following exceptions:
187187
* <ul>
188188
* <li>{@link org.cryptomator.cloudaccess.api.exceptions.NotFoundException} If no item exists for the given path</li>
189189
* <li>{@link CloudProviderException} in case of generic I/O errors</li>
190190
* </ul>
191191
*
192-
* @param node The remote path of the file or folder to delete.
193-
* @return CompletionStage completing successfully if node was deleted.
192+
* @param file The remote path of the file to delete.
193+
* @return CompletionStage completing successfully if file was deleted.
194194
*/
195-
CompletionStage<Void> delete(CloudPath node);
195+
CompletionStage<Void> deleteFile(CloudPath file);
196+
197+
/**
198+
* Recursively delete a folder.
199+
* <p>
200+
* The returned CompletionStage might complete exceptionally with one of the following exceptions:
201+
* <ul>
202+
* <li>{@link org.cryptomator.cloudaccess.api.exceptions.NotFoundException} If no item exists for the given path</li>
203+
* <li>{@link CloudProviderException} in case of generic I/O errors</li>
204+
* </ul>
205+
*
206+
* @param folder The remote path of the folder to delete.
207+
* @return CompletionStage completing successfully if folder was deleted.
208+
*/
209+
CompletionStage<Void> deleteFolder(CloudPath folder);
196210

197211
/**
198212
* Move a file or folder to a different location.

src/main/java/org/cryptomator/cloudaccess/localfs/LocalFsCloudProvider.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,25 @@ public CompletionStage<CloudPath> createFolder(CloudPath folder) {
195195
}
196196

197197
@Override
198-
public CompletionStage<Void> delete(CloudPath node) {
199-
Path path = resolve(node);
198+
public CompletionStage<Void> deleteFile(CloudPath file) {
199+
Path path = resolve(file);
200+
Lock l = lock.writeLock();
201+
l.lock();
202+
try {
203+
Files.delete(path);
204+
return CompletableFuture.completedFuture(null);
205+
} catch (NoSuchFileException e) {
206+
return CompletableFuture.failedFuture(new NotFoundException(e));
207+
} catch (IOException e) {
208+
return CompletableFuture.failedFuture(new CloudProviderException(e));
209+
} finally {
210+
l.unlock();
211+
}
212+
}
213+
214+
@Override
215+
public CompletionStage<Void> deleteFolder(CloudPath folder) {
216+
Path path = resolve(folder);
200217
Lock l = lock.writeLock();
201218
l.lock();
202219
try {

src/main/java/org/cryptomator/cloudaccess/vaultformat8/VaultFormat8ProviderDecorator.java

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -177,20 +177,22 @@ public CompletionStage<CloudPath> createFolder(CloudPath folder) {
177177
}
178178

179179
@Override
180-
public CompletionStage<Void> delete(CloudPath node) {
181-
return itemMetadata(node).thenCompose(cloudNode -> {
182-
if (cloudNode.getItemType() == CloudItemType.FILE) {
183-
return getC9rPath(node).thenCompose(ciphertextPath -> {
180+
public CompletionStage<Void> deleteFile(CloudPath file) {
181+
return itemMetadata(file) //
182+
.thenCompose(cloudNode -> getC9rPath(file)) //
183+
.thenCompose(ciphertextPath -> {
184184
fileHeaderCache.evict(ciphertextPath);
185-
return delegate.delete(ciphertextPath);
185+
return delegate.deleteFile(ciphertextPath);
186186
});
187-
} else {
188-
return deleteCiphertextDir(getDirPathFromClearTextDir(node)) //
189-
.thenCompose(ignored -> getC9rPath(node)) //
190-
.thenCompose(delegate::delete) //
191-
.thenRun(() -> dirIdCache.evictIncludingDescendants(node));
192-
}
193-
});
187+
}
188+
189+
@Override
190+
public CompletionStage<Void> deleteFolder(CloudPath folder) {
191+
return itemMetadata(folder) //
192+
.thenCompose(cloudNode -> deleteCiphertextDir(getDirPathFromClearTextDir(folder))) //
193+
.thenCompose(ignored -> getC9rPath(folder)) //
194+
.thenCompose(delegate::deleteFolder) //
195+
.thenRun(() -> dirIdCache.evictIncludingDescendants(folder));
194196
}
195197

196198
private CompletionStage<Void> deleteCiphertextDir(CompletionStage<CloudPath> dirPath) {
@@ -203,7 +205,7 @@ private CompletionStage<Void> deleteCiphertextDir(CompletionStage<CloudPath> dir
203205
var futures = result.map(CompletionStage::toCompletableFuture).toArray(CompletableFuture[]::new);
204206
return CompletableFuture.allOf(futures);
205207
}).thenCombine(dirPath, (unused, path) -> path) //
206-
.thenCompose(delegate::delete);
208+
.thenCompose(delegate::deleteFolder);
207209
}
208210

209211
@Override

src/main/java/org/cryptomator/cloudaccess/webdav/WebDavCloudProvider.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,13 @@ public CompletionStage<CloudPath> createFolder(CloudPath folder) {
6363
}
6464

6565
@Override
66-
public CompletionStage<Void> delete(CloudPath node) {
67-
return CompletableFuture.runAsync(() -> webDavClient.delete(node));
66+
public CompletionStage<Void> deleteFile(CloudPath file) {
67+
return CompletableFuture.runAsync(() -> webDavClient.delete(file));
68+
}
69+
70+
@Override
71+
public CompletionStage<Void> deleteFolder(CloudPath folder) {
72+
return CompletableFuture.runAsync(() -> webDavClient.delete(folder));
6873
}
6974

7075
@Override

src/test/java/org/cryptomator/cloudaccess/MetadataCachingProviderDecoratorTest.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,9 @@ public void testMoveFileToNewFileNotFound() {
225225
public void testDeleteFile() {
226226
decorator.itemMetadataCache.put(file4Metadata.getPath(), Optional.of(file4Metadata));
227227

228-
Mockito.when(cloudProvider.delete(file4Metadata.getPath())).thenReturn(CompletableFuture.completedFuture(null));
228+
Mockito.when(cloudProvider.deleteFile(file4Metadata.getPath())).thenReturn(CompletableFuture.completedFuture(null));
229229

230-
var futureResult = decorator.delete(file4Metadata.getPath());
230+
var futureResult = decorator.deleteFile(file4Metadata.getPath());
231231
Assertions.assertTimeoutPreemptively(Duration.ofMillis(100), () -> futureResult.toCompletableFuture().get());
232232

233233
Assertions.assertEquals(decorator.itemMetadataCache.size(), 0l);
@@ -238,9 +238,9 @@ public void testDeleteFile() {
238238
public void testDeleteFileNotFound() {
239239
decorator.itemMetadataCache.put(file4Metadata.getPath(), Optional.of(file4Metadata));
240240

241-
Mockito.when(cloudProvider.delete(file4Metadata.getPath())).thenReturn(CompletableFuture.failedFuture(new NotFoundException()));
241+
Mockito.when(cloudProvider.deleteFile(file4Metadata.getPath())).thenReturn(CompletableFuture.failedFuture(new NotFoundException()));
242242

243-
Assertions.assertThrows(NotFoundException.class, () -> decorator.delete(file4Metadata.getPath()).toCompletableFuture().join());
243+
Assertions.assertThrows(NotFoundException.class, () -> decorator.deleteFile(file4Metadata.getPath()).toCompletableFuture().join());
244244

245245
Assertions.assertEquals(decorator.itemMetadataCache.size(), 0l);
246246
}
@@ -250,9 +250,9 @@ public void testDeleteFileNotFound() {
250250
public void testDeleteFolder() {
251251
decorator.itemMetadataCache.put(dir1Metadata.getPath(), Optional.of(dir1Metadata));
252252

253-
Mockito.when(cloudProvider.delete(dir1Metadata.getPath())).thenReturn(CompletableFuture.completedFuture(null));
253+
Mockito.when(cloudProvider.deleteFolder(dir1Metadata.getPath())).thenReturn(CompletableFuture.completedFuture(null));
254254

255-
var futureResult = decorator.delete(dir1Metadata.getPath());
255+
var futureResult = decorator.deleteFolder(dir1Metadata.getPath());
256256
Assertions.assertTimeoutPreemptively(Duration.ofMillis(100), () -> futureResult.toCompletableFuture().get());
257257

258258
Assertions.assertEquals(decorator.itemMetadataCache.size(), 0l);

src/test/java/org/cryptomator/cloudaccess/localfs/LocalFsCloudProviderTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ public void testCreateFolder() {
189189
public void testDeleteFile() throws IOException {
190190
Files.createFile(root.resolve("file"));
191191

192-
var result = provider.delete(CloudPath.of("/file"));
192+
var result = provider.deleteFile(CloudPath.of("/file"));
193193
Assertions.assertTimeoutPreemptively(Duration.ofSeconds(1), () -> result.toCompletableFuture().get());
194194

195195
Assertions.assertTrue(Files.notExists(root.resolve("file")));
@@ -201,7 +201,7 @@ public void testDeleteFolder() throws IOException {
201201
Files.createDirectory(root.resolve("folder"));
202202
Files.createFile(root.resolve("folder/file"));
203203

204-
var result = provider.delete(CloudPath.of("/folder"));
204+
var result = provider.deleteFolder(CloudPath.of("/folder"));
205205
Assertions.assertTimeoutPreemptively(Duration.ofSeconds(1), () -> result.toCompletableFuture().get());
206206

207207
Assertions.assertTrue(Files.notExists(root.resolve("folder")));

src/test/java/org/cryptomator/cloudaccess/vaultformat8/VaultFormat8IntegrationTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,14 @@ public void testWriteThenReadFile() throws IOException {
6464

6565
@Test
6666
public void testInstantiateFormat8GCMCloudAccessWithoutVaultConfigFile() {
67-
localProvider.delete(CloudPath.of("/vaultconfig.jwt"));
67+
localProvider.deleteFile(CloudPath.of("/vaultconfig.jwt"));
6868
var exception = Assertions.assertThrows(CloudProviderException.class, () -> CloudAccess.vaultFormat8GCMCloudAccess(localProvider, CloudPath.of("/"), new byte[64]));
6969
Assertions.assertTrue(exception.getCause() instanceof NotFoundException);
7070
}
7171

7272
@Test
7373
public void testInstantiateFormat8GCMCloudAccessWithWrongVaultVersion() {
74-
localProvider.delete(CloudPath.of("/vaultconfig.jwt"));
74+
localProvider.deleteFile(CloudPath.of("/vaultconfig.jwt"));
7575
byte[] masterkey = new byte[64];
7676
Algorithm algorithm = Algorithm.HMAC256(masterkey);
7777
var token = JWT.create()
@@ -87,7 +87,7 @@ public void testInstantiateFormat8GCMCloudAccessWithWrongVaultVersion() {
8787

8888
@Test
8989
public void testInstantiateFormat8GCMCloudAccessWithWrongCiphermode() {
90-
localProvider.delete(CloudPath.of("/vaultconfig.jwt"));
90+
localProvider.deleteFile(CloudPath.of("/vaultconfig.jwt"));
9191
byte[] masterkey = new byte[64];
9292
Algorithm algorithm = Algorithm.HMAC256(masterkey);
9393
var token = JWT.create()
@@ -103,7 +103,7 @@ public void testInstantiateFormat8GCMCloudAccessWithWrongCiphermode() {
103103

104104
@Test
105105
public void testInstantiateFormat8GCMCloudAccessWithWrongKey() {
106-
localProvider.delete(CloudPath.of("/vaultconfig.jwt"));
106+
localProvider.deleteFile(CloudPath.of("/vaultconfig.jwt"));
107107
byte[] masterkey = new byte[64];
108108
Arrays.fill(masterkey, (byte) 15);
109109
Algorithm algorithm = Algorithm.HMAC256(masterkey);

src/test/java/org/cryptomator/cloudaccess/vaultformat8/VaultFormat8ProviderDecoratorTest.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -249,9 +249,9 @@ public void testDeleteFile() {
249249
Mockito.when(fileNameCryptor.encryptFilename(BaseEncoding.base64Url(), "File 4", dirIdRoot.getBytes())).thenReturn("file4");
250250

251251
Mockito.when(cloudProvider.itemMetadata(dataDir.resolve("00/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/file4.c9r"))).thenReturn(CompletableFuture.completedFuture(file4Metadata));
252-
Mockito.when(cloudProvider.delete(CloudPath.of("path/to/vault/d/00/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/file4.c9r"))).thenReturn(CompletableFuture.completedFuture(null));
252+
Mockito.when(cloudProvider.deleteFile(CloudPath.of("path/to/vault/d/00/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/file4.c9r"))).thenReturn(CompletableFuture.completedFuture(null));
253253

254-
var futureResult = decorator.delete(CloudPath.of("/File 4"));
254+
var futureResult = decorator.deleteFile(CloudPath.of("/File 4"));
255255
Assertions.assertTimeoutPreemptively(Duration.ofMillis(100), () -> futureResult.toCompletableFuture().get());
256256
}
257257

@@ -267,14 +267,14 @@ public void testDeleteSingleFolder() {
267267

268268
var dir2ItemList = new CloudItemList(List.of(), Optional.empty());
269269
Mockito.when(cloudProvider.listExhaustively(dataDir.resolve("22/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"))).thenReturn(CompletableFuture.completedFuture(dir2ItemList));
270-
Mockito.when(cloudProvider.delete(dataDir.resolve("22/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"))).thenReturn(CompletableFuture.completedFuture(null));
271-
Mockito.when(cloudProvider.delete(dir2Metadata.getPath())).thenReturn(CompletableFuture.completedFuture(null));
270+
Mockito.when(cloudProvider.deleteFolder(dataDir.resolve("22/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"))).thenReturn(CompletableFuture.completedFuture(null));
271+
Mockito.when(cloudProvider.deleteFolder(dir2Metadata.getPath())).thenReturn(CompletableFuture.completedFuture(null));
272272

273-
var futureResult = decorator.delete(CloudPath.of("/Directory 1/Directory 2/"));
273+
var futureResult = decorator.deleteFolder(CloudPath.of("/Directory 1/Directory 2/"));
274274
Assertions.assertTimeoutPreemptively(Duration.ofMillis(100), () -> futureResult.toCompletableFuture().get());
275275

276-
Mockito.verify(cloudProvider).delete(dataDir.resolve("22/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"));
277-
Mockito.verify(cloudProvider).delete(dir2Metadata.getPath());
276+
Mockito.verify(cloudProvider).deleteFolder(dataDir.resolve("22/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"));
277+
Mockito.verify(cloudProvider).deleteFolder(dir2Metadata.getPath());
278278
}
279279

280280
@Test
@@ -294,16 +294,16 @@ public void testDeleteFolderRecursively() {
294294
Mockito.when(cloudProvider.listExhaustively(dataDir.resolve("11/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"))).thenReturn(CompletableFuture.completedFuture(dir1ItemList));
295295
Mockito.when(cloudProvider.listExhaustively(dataDir.resolve("22/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"))).thenReturn(CompletableFuture.completedFuture(dir2ItemList));
296296

297-
Mockito.when(cloudProvider.delete(dataDir.resolve("11/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"))).thenReturn(CompletableFuture.completedFuture(null));
298-
Mockito.when(cloudProvider.delete(dataDir.resolve("22/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"))).thenReturn(CompletableFuture.completedFuture(null));
299-
Mockito.when(cloudProvider.delete(dir1Metadata.getPath())).thenReturn(CompletableFuture.completedFuture(null));
297+
Mockito.when(cloudProvider.deleteFolder(dataDir.resolve("11/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"))).thenReturn(CompletableFuture.completedFuture(null));
298+
Mockito.when(cloudProvider.deleteFolder(dataDir.resolve("22/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"))).thenReturn(CompletableFuture.completedFuture(null));
299+
Mockito.when(cloudProvider.deleteFolder(dir1Metadata.getPath())).thenReturn(CompletableFuture.completedFuture(null));
300300

301-
var futureResult = decorator.delete(CloudPath.of("/Directory 1"));
301+
var futureResult = decorator.deleteFolder(CloudPath.of("/Directory 1"));
302302
Assertions.assertTimeoutPreemptively(Duration.ofMillis(100), () -> futureResult.toCompletableFuture().get());
303303

304-
Mockito.verify(cloudProvider).delete(dataDir.resolve("11/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"));
305-
Mockito.verify(cloudProvider).delete(dataDir.resolve("22/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"));
306-
Mockito.verify(cloudProvider).delete(dir1Metadata.getPath());
304+
Mockito.verify(cloudProvider).deleteFolder(dataDir.resolve("11/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"));
305+
Mockito.verify(cloudProvider).deleteFolder(dataDir.resolve("22/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"));
306+
Mockito.verify(cloudProvider).deleteFolder(dir1Metadata.getPath());
307307
}
308308

309309
@DisplayName("create(\"/Directory 3/\")")

src/test/java/org/cryptomator/cloudaccess/webdav/WebDavCloudProviderTestIT.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ public void testCreateFolder() throws InterruptedException {
237237
public void testDeleteFile() throws InterruptedException {
238238
server.enqueue(getInterceptedResponse());
239239

240-
Assertions.assertTimeoutPreemptively(timeout, () -> provider.delete(CloudPath.of("/foo.txt")).toCompletableFuture().join());
240+
Assertions.assertTimeoutPreemptively(timeout, () -> provider.deleteFile(CloudPath.of("/foo.txt")).toCompletableFuture().join());
241241

242242
var rq = Assertions.assertTimeoutPreemptively(timeout, () -> server.takeRequest());
243243
Assertions.assertEquals("DELETE", rq.getMethod());
@@ -249,7 +249,7 @@ public void testDeleteFile() throws InterruptedException {
249249
public void testDeleteFolder() throws InterruptedException {
250250
server.enqueue(getInterceptedResponse());
251251

252-
Assertions.assertTimeoutPreemptively(timeout, () -> provider.delete(CloudPath.of("/foo")).toCompletableFuture().join());
252+
Assertions.assertTimeoutPreemptively(timeout, () -> provider.deleteFolder(CloudPath.of("/foo")).toCompletableFuture().join());
253253

254254
var rq = Assertions.assertTimeoutPreemptively(timeout, () -> server.takeRequest());
255255
Assertions.assertEquals("DELETE", rq.getMethod());

0 commit comments

Comments
 (0)