From a37fe7ada1282096b94ef01efb45cdb978ab7796 Mon Sep 17 00:00:00 2001 From: Siddartha Boddu Date: Sun, 18 May 2025 03:08:03 +0530 Subject: [PATCH] Added ability to use Caffeine for Caching, enabling additional features like eviction to be used which are not supported by existing ConcurrenctHashMap caching (#45) - Added CacheManager interface to be able to use ConcurrentHashMap or Caffeine for caching. - Added ConcurrentHashMapCacheManager class which uses ConcurrentHashMap for caching, this will be default behaviour unless client uses CaffeineCacheManager. - Added CaffeineCacheManager which uses Caffeine for caching. --- build.gradle | 3 +- .../github/jamsesso/jsonlogic/JsonLogic.java | 15 ++++++- .../jsonlogic/cache/CacheManager.java | 30 +++++++++++++ .../jsonlogic/cache/CaffeineCacheManager.java | 44 +++++++++++++++++++ .../cache/ConcurrentHashMapCacheManager.java | 35 +++++++++++++++ .../jsonlogic/CaffeineCacheJsonLogicTest.java | 28 ++++++++++++ 6 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 src/main/java/io/github/jamsesso/jsonlogic/cache/CacheManager.java create mode 100644 src/main/java/io/github/jamsesso/jsonlogic/cache/CaffeineCacheManager.java create mode 100644 src/main/java/io/github/jamsesso/jsonlogic/cache/ConcurrentHashMapCacheManager.java create mode 100644 src/test/java/io/github/jamsesso/jsonlogic/CaffeineCacheJsonLogicTest.java diff --git a/build.gradle b/build.gradle index d3cd452..70fa290 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { } group "io.github.jamsesso" -version "1.1.1-SNAPSHOT" +version "1.1.2-SNAPSHOT" sourceCompatibility = 1.8 targetCompatibility = 1.8 @@ -17,6 +17,7 @@ repositories { dependencies { compile "com.google.code.gson:gson:2.8.5" testCompile "junit:junit:4.12" + implementation "com.github.ben-manes.caffeine:caffeine:2.9.3" } task javadocJar(type: Jar, dependsOn: javadoc) { diff --git a/src/main/java/io/github/jamsesso/jsonlogic/JsonLogic.java b/src/main/java/io/github/jamsesso/jsonlogic/JsonLogic.java index fa25f2f..b42cf62 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/JsonLogic.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/JsonLogic.java @@ -1,7 +1,10 @@ package io.github.jamsesso.jsonlogic; import io.github.jamsesso.jsonlogic.ast.JsonLogicNode; +import io.github.jamsesso.jsonlogic.ast.JsonLogicParseException; import io.github.jamsesso.jsonlogic.ast.JsonLogicParser; +import io.github.jamsesso.jsonlogic.cache.CacheManager; +import io.github.jamsesso.jsonlogic.cache.ConcurrentHashMapCacheManager; import io.github.jamsesso.jsonlogic.evaluator.JsonLogicEvaluator; import io.github.jamsesso.jsonlogic.evaluator.JsonLogicExpression; import io.github.jamsesso.jsonlogic.evaluator.expressions.*; @@ -12,11 +15,21 @@ import java.util.function.Function; public final class JsonLogic { - private final Map parseCache = new ConcurrentHashMap<>(); + private final CacheManager parseCache; private final Map expressions = new ConcurrentHashMap<>(); private JsonLogicEvaluator evaluator; public JsonLogic() { + this.parseCache = new ConcurrentHashMapCacheManager<>(); + initializeOperations(); + } + + public JsonLogic(final CacheManager parseCache) { + this.parseCache = parseCache; + initializeOperations(); + } + + private void initializeOperations() { // Add default operations addOperation(MathExpression.ADD); addOperation(MathExpression.SUBTRACT); diff --git a/src/main/java/io/github/jamsesso/jsonlogic/cache/CacheManager.java b/src/main/java/io/github/jamsesso/jsonlogic/cache/CacheManager.java new file mode 100644 index 0000000..f0582cf --- /dev/null +++ b/src/main/java/io/github/jamsesso/jsonlogic/cache/CacheManager.java @@ -0,0 +1,30 @@ +package io.github.jamsesso.jsonlogic.cache; + +/** + * CacheManager interface for managing cache operations. + * + * @param the type of keys maintained by this cache + * @param the type of cached values + */ +public interface CacheManager { + /** + * Checks if the cache contains a value for the specified key. + * @param key the key to check + * @return true if the cache contains the key, false otherwise + */ + boolean containsKey(K key); + + /** + * Puts a value in the cache with the specified key. + * @param key the key to associate with the value + * @param value the value to cache + */ + void put(K key, V value); + + /** + * Retrieves a value from the cache for the specified key. + * @param key the key to look up + * @return the cached value, or null if not found + */ + V get(K key); +} diff --git a/src/main/java/io/github/jamsesso/jsonlogic/cache/CaffeineCacheManager.java b/src/main/java/io/github/jamsesso/jsonlogic/cache/CaffeineCacheManager.java new file mode 100644 index 0000000..8a3c0bc --- /dev/null +++ b/src/main/java/io/github/jamsesso/jsonlogic/cache/CaffeineCacheManager.java @@ -0,0 +1,44 @@ +package io.github.jamsesso.jsonlogic.cache; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + +/** + * CaffeineCacheManager is a cache manager that uses Caffeine for caching. + * It provides methods to check if a key exists, put a value in the cache, + * and retrieve a value from the cache. + * + * @param the type of keys maintained by this cache + * @param the type of cached values + */ +public class CaffeineCacheManager implements CacheManager { + private final Cache cache; + + public CaffeineCacheManager() { + this.cache = Caffeine.newBuilder().build(); + } + + /** + * Constructs a CaffeineCacheManager with the specified maximum capacity. + * + * @param maxCapacity the maximum number of entries the cache can hold + */ + public CaffeineCacheManager(int maxCapacity) { + this.cache = Caffeine.newBuilder().maximumSize(maxCapacity).build(); + } + + @Override + public boolean containsKey(K key) { + return cache.asMap().containsKey(key); + } + + @Override + public void put(K key, V value) { + cache.put(key, value); + } + + @Override + public V get(K key) { + return cache.getIfPresent(key); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/jamsesso/jsonlogic/cache/ConcurrentHashMapCacheManager.java b/src/main/java/io/github/jamsesso/jsonlogic/cache/ConcurrentHashMapCacheManager.java new file mode 100644 index 0000000..fc8fb8c --- /dev/null +++ b/src/main/java/io/github/jamsesso/jsonlogic/cache/ConcurrentHashMapCacheManager.java @@ -0,0 +1,35 @@ +package io.github.jamsesso.jsonlogic.cache; + +import java.util.concurrent.ConcurrentHashMap; + +/** + * ConcurrentHashMapCacheManager is a cache manager that uses ConcurrentHashMap for caching. + * It provides methods to check if a key exists, put a value in the cache, + * and retrieve a value from the cache. + * + * @param the type of keys maintained by this cache + * @param the type of cached values + */ +public class ConcurrentHashMapCacheManager implements CacheManager { + private final ConcurrentHashMap cache; + + public ConcurrentHashMapCacheManager() { + this.cache = new ConcurrentHashMap<>(); + } + + @Override + public boolean containsKey(K key) { + return cache.containsKey(key); + } + + @Override + public void put(K key, V value) { + cache.put(key, value); + } + + @Override + public V get(K key) { + return cache.get(key); + } + +} diff --git a/src/test/java/io/github/jamsesso/jsonlogic/CaffeineCacheJsonLogicTest.java b/src/test/java/io/github/jamsesso/jsonlogic/CaffeineCacheJsonLogicTest.java new file mode 100644 index 0000000..aac3f98 --- /dev/null +++ b/src/test/java/io/github/jamsesso/jsonlogic/CaffeineCacheJsonLogicTest.java @@ -0,0 +1,28 @@ +package io.github.jamsesso.jsonlogic; + +import org.junit.Test; + +import io.github.jamsesso.jsonlogic.cache.CaffeineCacheManager; + +import static org.junit.Assert.assertEquals; + +public class CaffeineCacheJsonLogicTest { + private static final JsonLogic jsonLogic = new JsonLogic(new CaffeineCacheManager<>()); + private static final JsonLogic jsonLogicWithMaxCacheSize = new JsonLogic(new CaffeineCacheManager<>(1000)); + + + @Test + public void testEmptyArray() throws JsonLogicException { + assertEquals(false, jsonLogic.apply("{\"all\": [[], {\">\": [{\"var\": \"\"}, 0]}]}", null)); + assertEquals(false, jsonLogicWithMaxCacheSize.apply("{\"all\": [[], {\">\": [{\"var\": \"\"}, 0]}]}", null)); + } + + @Test + public void testAll() throws JsonLogicException { + assertEquals(true, jsonLogic.apply("{\"all\": [[1, 2, 3], {\">\": [{\"var\": \"\"}, 0]}]}", null)); + assertEquals(false, jsonLogic.apply("{\"all\": [[1, 2, 3], {\">\": [{\"var\": \"\"}, 1]}]}", null)); + + assertEquals(true, jsonLogicWithMaxCacheSize.apply("{\"all\": [[1, 2, 3], {\">\": [{\"var\": \"\"}, 0]}]}", null)); + assertEquals(false, jsonLogicWithMaxCacheSize.apply("{\"all\": [[1, 2, 3], {\">\": [{\"var\": \"\"}, 1]}]}", null)); + } +}