diff --git a/src/main/java/org/cryptomator/common/vaults/Vault.java b/src/main/java/org/cryptomator/common/vaults/Vault.java index 37fb53f10..bac33151f 100644 --- a/src/main/java/org/cryptomator/common/vaults/Vault.java +++ b/src/main/java/org/cryptomator/common/vaults/Vault.java @@ -34,7 +34,6 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Named; -import javafx.application.Platform; import javafx.beans.Observable; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; @@ -260,9 +259,7 @@ public class Vault { private void consumeVaultEvent(FilesystemEvent e) { - Platform.runLater(() -> { - vaultEventsMap.put(this, e); - }); + vaultEventsMap.enque(this, e); } // ****************************************************************************** diff --git a/src/main/java/org/cryptomator/event/VaultEventsMap.java b/src/main/java/org/cryptomator/event/VaultEventsMap.java index bbaa5475b..da46f3c86 100644 --- a/src/main/java/org/cryptomator/event/VaultEventsMap.java +++ b/src/main/java/org/cryptomator/event/VaultEventsMap.java @@ -1,6 +1,5 @@ package org.cryptomator.event; -import org.cryptomator.common.ObservableMapDecorator; import org.cryptomator.common.vaults.Vault; import org.cryptomator.cryptofs.event.BrokenDirFileEvent; import org.cryptomator.cryptofs.event.BrokenFileNodeEvent; @@ -11,45 +10,177 @@ import org.cryptomator.cryptofs.event.FilesystemEvent; import javax.inject.Inject; import javax.inject.Singleton; +import javafx.application.Platform; import javafx.collections.FXCollections; +import javafx.collections.MapChangeListener; +import javafx.collections.ObservableMap; import java.nio.file.Path; import java.util.List; import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; -/** - * Map containing {@link VaultEvent}s. - * The map is keyed by three elements: - *
- * Use {@link VaultEventsMap#put(Vault, FilesystemEvent)} to add an element and {@link VaultEventsMap#remove(Vault, FilesystemEvent)} to remove it. - *
- * The map is size restricted to {@value MAX_SIZE} elements. If a _new_ element (i.e. not already present) is added, the least recently added is removed.
- */
@Singleton
-public class VaultEventsMap extends ObservableMapDecorator
+ * To identify the event, a similar event (in the sense of map key) is given.
+ * Must be executed on the JavaFX application thread
+ *
+ * @param v Vault where the event occurred
+ * @param similar A similar {@link FilesystemEvent} (same class, same idPath)
+ * @return the removed {@link Value}
+ */
+ public Value remove(Vault v, FilesystemEvent similar) {
+ if (!Platform.isFxApplicationThread()) {
+ throw new IllegalStateException("Map removal must be performed on JavaFX application thread");
+ }
+ var key = computeKey(v, similar);
+ lruCache.remove(key);
+ return map.remove(key);
+ }
+
+ public void clear() {
+ if (!Platform.isFxApplicationThread()) {
+ throw new IllegalStateException("Map removal must be performed on JavaFX application thread");
+ }
+ lruCache.clear();
+ map.clear();
+ }
+
+ /**
+ * Flushes all changes from the queue into the map
+ */
+ private synchronized void flush() {
+ //Lock queue
+ var latch = new CountDownLatch(1);
+ Platform.runLater(() -> {
+ queue.forEach(this::updateMap);
+ queue.clear();
+ latch.countDown();
+ });
+ try {
+ latch.await();
+ queueHasElements.set(false);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ /**
+ * Updates a single map entry
+ *
+ * @param k Key of the entry to update
+ * @param v Value of the entry to update
+ */
+ private void updateMap(Key k, Value v) {
+ var entry = map.get(k);
+ if (entry == null) {
+ if (map.size() == MAX_MAP_SIZE) {
+ var toRemove = lruCache.first();
+ lruCache.remove(toRemove);
+ map.remove(toRemove);
+ }
+ map.put(k, v);
+ lruCache.add(k);
+ } else {
+ lruCache.remove(k);
+ map.put(k, new Value(v.mostRecentEvent, entry.count + v.count));
+ lruCache.add(k); //correct, because cache-sorting uses the map in comparsionMethod
+ }
+ }
+
+ /* Observability */
+
+ public void addListener(MapChangeListener super Key, ? super Value> mapChangeListener) {
+ map.addListener(mapChangeListener);
+ }
+
+ public void removeListener(MapChangeListener super Key, ? super Value> mapChangeListener) {
+ map.removeListener(mapChangeListener);
+ }
+
+
+ /* Internal stuff */
/**
* Comparsion method for the lru cache. During comparsion the map is accessed.
@@ -59,9 +190,9 @@ public class VaultEventsMap extends ObservableMapDecorator