From 2a56414b40fbfba89d677e78697d065f815b76e6 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 31 Aug 2024 09:06:54 -0700 Subject: [PATCH] WIP --- .../world/bentobox/bentobox/BentoBox.java | 2 + .../bentobox/util/heads/ExpiringMap.java | 74 +++++++++++++++++++ .../bentobox/util/heads/HeadGetter.java | 16 +++- 3 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 src/main/java/world/bentobox/bentobox/util/heads/ExpiringMap.java diff --git a/src/main/java/world/bentobox/bentobox/BentoBox.java b/src/main/java/world/bentobox/bentobox/BentoBox.java index 75a7c07a8..650d5e4a5 100644 --- a/src/main/java/world/bentobox/bentobox/BentoBox.java +++ b/src/main/java/world/bentobox/bentobox/BentoBox.java @@ -324,6 +324,8 @@ public class BentoBox extends JavaPlugin implements Listener { // Stop all async database tasks shutdown = true; + HeadGetter.shutdown(); + if (addonsManager != null) { addonsManager.disableAddons(); } diff --git a/src/main/java/world/bentobox/bentobox/util/heads/ExpiringMap.java b/src/main/java/world/bentobox/bentobox/util/heads/ExpiringMap.java new file mode 100644 index 000000000..95dcb3c1c --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/util/heads/ExpiringMap.java @@ -0,0 +1,74 @@ +package world.bentobox.bentobox.util.heads; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; + +public class ExpiringMap { + private final Map map; + private final ScheduledExecutorService scheduler; + private final long expirationTime; + + public ExpiringMap(long expirationTime, TimeUnit timeUnit) { + this.map = new ConcurrentHashMap<>(); + this.scheduler = Executors.newSingleThreadScheduledExecutor(); + this.expirationTime = timeUnit.toMillis(expirationTime); + } + + public void put(K key, V value) { + map.put(key, value); + scheduleRemoval(key); + } + + public boolean containsKey(K key) { + return map.containsKey(key); + } + + public V get(K key) { + return map.get(key); + } + + public V remove(K key) { + return map.remove(key); + } + + public int size() { + return map.size(); + } + + public V computeIfAbsent(K key, Function mappingFunction) { + return map.computeIfAbsent(key, k -> { + V value = mappingFunction.apply(k); + scheduleRemoval(k); + return value; + }); + } + + private void scheduleRemoval(final K key) { + scheduler.schedule(() -> map.remove(key), expirationTime, TimeUnit.MILLISECONDS); + } + + public void shutdown() { + scheduler.shutdown(); + } + /* + public static void main(String[] args) throws InterruptedException { + ExpiringMap expiringMap = new ExpiringMap<>(5, TimeUnit.SECONDS); + + expiringMap.put("key1", "value1"); + System.out.println("Initial size: " + expiringMap.size()); // Should print 1 + + // Using computeIfAbsent + String value = expiringMap.computeIfAbsent("key2", k -> "computedValue"); + System.out.println("Computed value for key2: " + value); // Should print "computedValue" + System.out.println("Size after computeIfAbsent: " + expiringMap.size()); // Should print 2 + + Thread.sleep(6000); + System.out.println("Size after 6 seconds: " + expiringMap.size()); // Should print 0 + + expiringMap.shutdown(); + }*/ +} diff --git a/src/main/java/world/bentobox/bentobox/util/heads/HeadGetter.java b/src/main/java/world/bentobox/bentobox/util/heads/HeadGetter.java index e45e199a7..b2c9c716c 100644 --- a/src/main/java/world/bentobox/bentobox/util/heads/HeadGetter.java +++ b/src/main/java/world/bentobox/bentobox/util/heads/HeadGetter.java @@ -5,13 +5,12 @@ import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.Base64; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; import java.util.Queue; import java.util.Set; import java.util.UUID; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.bukkit.Bukkit; @@ -36,7 +35,7 @@ public class HeadGetter { /** * Local cache for storing player heads. */ - private static final Map cachedHeads = new HashMap<>(); + private static final ExpiringMap cachedHeads = new ExpiringMap<>(1, TimeUnit.HOURS); /** * Local cache for storing requested names and items which must be updated. @@ -46,7 +45,8 @@ public class HeadGetter { /** * Requesters of player heads. */ - private static final Map> headRequesters = new HashMap<>(); + private static final ExpiringMap> headRequesters = new ExpiringMap<>(10, + TimeUnit.SECONDS); private static final String TEXTURES = "textures"; @@ -65,6 +65,14 @@ public class HeadGetter { this.runPlayerHeadGetter(); } + /** + * Shutdown the schedulers + */ + public static void shutdown() { + cachedHeads.shutdown(); + headRequesters.shutdown(); + } + /** * @param panelItem - head to update * @param requester - callback class