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

Commit 7ada7ba

Browse files
committed
Merge branch 'feature/#4-sync-LocalFsProvider' into release/0.2.0
2 parents 1ca2ce4 + 3300492 commit 7ada7ba

File tree

1 file changed

+45
-1
lines changed

1 file changed

+45
-1
lines changed

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

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,31 @@
3737
import java.util.Optional;
3838
import java.util.concurrent.CompletableFuture;
3939
import java.util.concurrent.CompletionStage;
40-
40+
import java.util.concurrent.locks.Lock;
41+
import java.util.concurrent.locks.ReadWriteLock;
42+
import java.util.concurrent.locks.ReentrantReadWriteLock;
43+
44+
/**
45+
* CloudProvider implementation to mirror a folder in the local filesystem.
46+
* <p>
47+
* This class is mainly for testing purposes and therefore aims for correctness, not performance.
48+
* All filesystem altering operations (create, delete, move and write) will be executed exclusively and blocking,
49+
* while all fs quering operations are performed simultanously.
50+
*/
4151
public class LocalFsCloudProvider implements CloudProvider {
4252

4353
private static final CloudPath ABS_ROOT = CloudPath.of("/");
4454

4555
private final Path root;
4656

57+
/**
58+
* Lock to ensure that any operation always performed on a consistent filesystem, i.e. no pending fs-altering operation exists.
59+
*/
60+
private final ReadWriteLock lock;
61+
4762
public LocalFsCloudProvider(Path root) {
4863
this.root = root;
64+
this.lock = new ReentrantReadWriteLock();
4965
}
5066

5167
private Path resolve(CloudPath cloudPath) {
@@ -64,6 +80,8 @@ private CloudItemMetadata createMetadata(Path fullPath, BasicFileAttributes attr
6480
@Override
6581
public CompletionStage<CloudItemMetadata> itemMetadata(CloudPath node) {
6682
Path path = resolve(node);
83+
Lock l = lock.readLock();
84+
l.lock();
6785
try {
6886
var attr = Files.readAttributes(path, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
6987
var metadata = createMetadata(path, attr);
@@ -72,12 +90,16 @@ public CompletionStage<CloudItemMetadata> itemMetadata(CloudPath node) {
7290
return CompletableFuture.failedFuture(new NotFoundException(e));
7391
} catch (IOException e) {
7492
return CompletableFuture.failedFuture(new CloudProviderException(e));
93+
} finally {
94+
l.unlock();
7595
}
7696
}
7797

7898
@Override
7999
public CompletionStage<CloudItemList> list(CloudPath folder, Optional<String> pageToken) {
80100
Path folderPath = resolve(folder);
101+
Lock l = lock.readLock();
102+
l.lock();
81103
try {
82104
List<CloudItemMetadata> items = new ArrayList<>();
83105
var provider = this;
@@ -96,12 +118,16 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
96118
return CompletableFuture.failedFuture(new TypeMismatchException(e));
97119
} catch (IOException e) {
98120
return CompletableFuture.failedFuture(new CloudProviderException(e));
121+
} finally {
122+
l.unlock();
99123
}
100124
}
101125

102126
@Override
103127
public CompletionStage<InputStream> read(CloudPath file, long offset, long count, ProgressListener progressListener) {
104128
Path filePath = resolve(file);
129+
Lock l = lock.readLock();
130+
l.lock();
105131
try {
106132
var ch = Files.newByteChannel(filePath, StandardOpenOption.READ);
107133
ch.position(offset);
@@ -110,6 +136,8 @@ public CompletionStage<InputStream> read(CloudPath file, long offset, long count
110136
return CompletableFuture.failedFuture(new NotFoundException(e));
111137
} catch (IOException e) {
112138
return CompletableFuture.failedFuture(new CloudProviderException(e));
139+
} finally {
140+
l.unlock();
113141
}
114142
}
115143

@@ -120,6 +148,8 @@ public CompletionStage<CloudItemMetadata> write(CloudPath file, boolean replace,
120148
? EnumSet.of(StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)
121149
: EnumSet.of(StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
122150

151+
Lock l = lock.writeLock();
152+
l.lock();
123153
try (var ch = FileChannel.open(filePath, options)) {
124154
var tmpSize = ch.transferFrom(Channels.newChannel(data), 0, Long.MAX_VALUE);
125155
var modifiedDate = Files.getLastModifiedTime(filePath).toInstant();
@@ -131,39 +161,51 @@ public CompletionStage<CloudItemMetadata> write(CloudPath file, boolean replace,
131161
return CompletableFuture.failedFuture(new AlreadyExistsException(e));
132162
} catch (IOException e) {
133163
return CompletableFuture.failedFuture(new CloudProviderException(e));
164+
} finally {
165+
l.unlock();
134166
}
135167
}
136168

137169
@Override
138170
public CompletionStage<CloudPath> createFolder(CloudPath folder) {
139171
Path folderPath = resolve(folder);
172+
Lock l = lock.writeLock();
173+
l.lock();
140174
try {
141175
Files.createDirectory(folderPath);
142176
return CompletableFuture.completedFuture(folder);
143177
} catch (FileAlreadyExistsException e) {
144178
return CompletableFuture.failedFuture(new AlreadyExistsException(e));
145179
} catch (IOException e) {
146180
return CompletableFuture.failedFuture(new CloudProviderException(e));
181+
} finally {
182+
l.unlock();
147183
}
148184
}
149185

150186
@Override
151187
public CompletionStage<Void> delete(CloudPath node) {
152188
Path path = resolve(node);
189+
Lock l = lock.writeLock();
190+
l.lock();
153191
try {
154192
MoreFiles.deleteRecursively(path, RecursiveDeleteOption.ALLOW_INSECURE);
155193
return CompletableFuture.completedFuture(null);
156194
} catch (NoSuchFileException e) {
157195
return CompletableFuture.failedFuture(new NotFoundException(e));
158196
} catch (IOException e) {
159197
return CompletableFuture.failedFuture(new CloudProviderException(e));
198+
} finally {
199+
l.unlock();
160200
}
161201
}
162202

163203
@Override
164204
public CompletionStage<CloudPath> move(CloudPath source, CloudPath target, boolean replace) {
165205
Path src = resolve(source);
166206
Path dst = resolve(target);
207+
Lock l = lock.writeLock();
208+
l.lock();
167209
try {
168210
var options = replace ? EnumSet.of(StandardCopyOption.REPLACE_EXISTING) : EnumSet.noneOf(StandardCopyOption.class);
169211
Files.move(src, dst, options.toArray(CopyOption[]::new));
@@ -174,6 +216,8 @@ public CompletionStage<CloudPath> move(CloudPath source, CloudPath target, boole
174216
return CompletableFuture.failedFuture(new AlreadyExistsException(e));
175217
} catch (IOException e) {
176218
return CompletableFuture.failedFuture(new CloudProviderException(e));
219+
} finally {
220+
l.unlock();
177221
}
178222
}
179223
}

0 commit comments

Comments
 (0)