This is an automated email from the ASF dual-hosted git repository.

adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new 1f76a8aeb2 FINERACT-2262: Improve cache handling and add customization 
option
1f76a8aeb2 is described below

commit 1f76a8aeb2c91a2e956d46b430a8622b750ae6ff
Author: Adam Saghy <[email protected]>
AuthorDate: Mon Apr 14 20:43:38 2025 +0200

    FINERACT-2262: Improve cache handling and add customization option
---
 .../groovy/org.apache.fineract.dependencies.gradle |   2 +
 .../core/config/FineractProperties.java            |  19 ++++
 fineract-provider/dependencies.gradle              |   1 +
 .../core/config/cache/CacheConfig.java             | 112 +++++++++++----------
 .../src/main/resources/application.properties      |   7 ++
 .../src/test/resources/application-test.properties |   4 +
 6 files changed, 91 insertions(+), 54 deletions(-)

diff --git a/buildSrc/src/main/groovy/org.apache.fineract.dependencies.gradle 
b/buildSrc/src/main/groovy/org.apache.fineract.dependencies.gradle
index 1a21f4ce15..01e25361f2 100644
--- a/buildSrc/src/main/groovy/org.apache.fineract.dependencies.gradle
+++ b/buildSrc/src/main/groovy/org.apache.fineract.dependencies.gradle
@@ -247,5 +247,7 @@ dependencyManagement {
         dependency 'io.cucumber:cucumber-java8:7.20.1'
         dependency 'io.cucumber:cucumber-junit-platform-engine:7.20.1'
         dependency 'io.cucumber:cucumber-spring:7.20.1'
+
+        dependency 'org.reflections:reflections:0.10.2'
     }
 }
diff --git 
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/config/FineractProperties.java
 
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/config/FineractProperties.java
index 961d5a5822..69de793278 100644
--- 
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/config/FineractProperties.java
+++ 
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/config/FineractProperties.java
@@ -19,6 +19,7 @@
 
 package org.apache.fineract.infrastructure.core.config;
 
+import java.time.Duration;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
@@ -81,6 +82,8 @@ public class FineractProperties {
 
     private FineractSqlValidationProperties sqlValidation;
 
+    private FineractCache cache;
+
     @Getter
     @Setter
     public static class FineractTenantProperties {
@@ -578,4 +581,20 @@ public class FineractProperties {
         private String name;
         private String pattern;
     }
+
+    @Getter
+    @Setter
+    public static class FineractCache {
+
+        private FineractCacheDetails defaultTemplate;
+        private Map<String, FineractCacheDetails> customTemplates = new 
HashMap<>();
+    }
+
+    @Getter
+    @Setter
+    public static class FineractCacheDetails {
+
+        private Duration ttl;
+        private Integer maximumEntries;
+    }
 }
diff --git a/fineract-provider/dependencies.gradle 
b/fineract-provider/dependencies.gradle
index 7af3a59982..19569b2158 100644
--- a/fineract-provider/dependencies.gradle
+++ b/fineract-provider/dependencies.gradle
@@ -113,6 +113,7 @@ dependencies {
             'org.mapstruct:mapstruct',
 
             'io.github.resilience4j:resilience4j-spring-boot3',
+            'org.reflections:reflections',
             )
 
     implementation 'software.amazon.msk:aws-msk-iam-auth'
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/cache/CacheConfig.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/cache/CacheConfig.java
index 3f90d0646f..392e878290 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/cache/CacheConfig.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/cache/CacheConfig.java
@@ -19,23 +19,38 @@
 
 package org.apache.fineract.infrastructure.core.config.cache;
 
+import java.lang.reflect.Method;
 import java.time.Duration;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
 import javax.cache.CacheManager;
 import javax.cache.Caching;
 import javax.cache.spi.CachingProvider;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.infrastructure.core.config.FineractProperties;
 import org.ehcache.config.builders.CacheConfigurationBuilder;
 import org.ehcache.config.builders.ExpiryPolicyBuilder;
 import org.ehcache.config.builders.ResourcePoolsBuilder;
 import org.ehcache.jsr107.Eh107Configuration;
+import org.reflections.Reflections;
+import org.reflections.scanners.Scanners;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
 import org.springframework.cache.jcache.JCacheCacheManager;
 import org.springframework.cache.support.NoOpCacheManager;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
 @Configuration
+@Slf4j
 public class CacheConfig {
 
     public static final String CONFIG_BY_NAME_CACHE_NAME = "configByName";
+    @Autowired
+    private FineractProperties fineractProperties;
 
     @Bean
     public TransactionBoundCacheManager defaultCacheManager(JCacheCacheManager 
ehCacheManager) {
@@ -56,63 +71,52 @@ public class CacheConfig {
     private CacheManager getInternalEhCacheManager() {
         CachingProvider provider = Caching.getCachingProvider();
         CacheManager cacheManager = provider.getCacheManager();
-
-        javax.cache.configuration.Configuration<Object, Object> 
defaultTemplate = Eh107Configuration.fromEhcacheCacheConfiguration(
-                
CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, 
Object.class, ResourcePoolsBuilder.heap(10000))
-                        
.withExpiry(ExpiryPolicyBuilder.noExpiration()).build());
-
-        if (cacheManager.getCache("users") == null) {
-            cacheManager.createCache("users", defaultTemplate);
-        }
-        if (cacheManager.getCache("usersByUsername") == null) {
-            cacheManager.createCache("usersByUsername", defaultTemplate);
-        }
-        if (cacheManager.getCache("tenantsById") == null) {
-            cacheManager.createCache("tenantsById", defaultTemplate);
-        }
-        if (cacheManager.getCache("offices") == null) {
-            cacheManager.createCache("offices", defaultTemplate);
-        }
-        if (cacheManager.getCache("officesForDropdown") == null) {
-            cacheManager.createCache("officesForDropdown", defaultTemplate);
-        }
-        if (cacheManager.getCache("officesById") == null) {
-            cacheManager.createCache("officesById", defaultTemplate);
-        }
-        if (cacheManager.getCache("charges") == null) {
-            cacheManager.createCache("charges", defaultTemplate);
-        }
-        if (cacheManager.getCache("funds") == null) {
-            cacheManager.createCache("funds", defaultTemplate);
-        }
-        if (cacheManager.getCache("code_values") == null) {
-            cacheManager.createCache("code_values", defaultTemplate);
-        }
-        if (cacheManager.getCache("codes") == null) {
-            cacheManager.createCache("codes", defaultTemplate);
-        }
-        if (cacheManager.getCache("hooks") == null) {
-            cacheManager.createCache("hooks", defaultTemplate);
-        }
-        if (cacheManager.getCache("tfConfig") == null) {
-            cacheManager.createCache("tfConfig", defaultTemplate);
-        }
-        if (cacheManager.getCache(CONFIG_BY_NAME_CACHE_NAME) == null) {
-            cacheManager.createCache(CONFIG_BY_NAME_CACHE_NAME, 
defaultTemplate);
-        }
-
-        javax.cache.configuration.Configuration<Object, Object> 
accessTokenTemplate = Eh107Configuration.fromEhcacheCacheConfiguration(
-                
CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, 
Object.class, ResourcePoolsBuilder.heap(10000))
-                        
.withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofHours(2))).build());
-
-        if (cacheManager.getCache("userTFAccessToken") == null) {
-            cacheManager.createCache("userTFAccessToken", accessTokenTemplate);
+        // Default cache configuration template
+        Duration defaultTimeToLive = 
fineractProperties.getCache().getDefaultTemplate().getTtl();
+        Integer defaultMaxEntries = 
fineractProperties.getCache().getDefaultTemplate().getMaximumEntries();
+        javax.cache.configuration.Configuration<Object, Object> 
defaultTemplate = generateCacheConfiguration(defaultMaxEntries,
+                defaultTimeToLive);
+        // Scan all packages (entire classpath)
+        Reflections reflections = new Reflections(
+                new 
org.reflections.util.ConfigurationBuilder().forPackages("").addScanners(Scanners.MethodsAnnotated));
+        // Find all methods annotated with @Cacheable
+        Set<Method> annotatedMethods = 
reflections.getMethodsAnnotatedWith(Cacheable.class);
+        Set<String> cacheNames = annotatedMethods.stream().map(method -> 
method.getAnnotation(Cacheable.class))
+                .flatMap(annotation -> 
Arrays.stream(annotation.value())).collect(Collectors.toSet());
+        // Register the caches into the cache manager
+        cacheNames.forEach(cacheName -> {
+            if (cacheManager.getCache(cacheName) == null) {
+                javax.cache.configuration.Configuration<Object, Object> 
configurationTemplate = generateCustomCacheConfiguration(cacheName,
+                        defaultTemplate, defaultTimeToLive, defaultMaxEntries);
+                cacheManager.createCache(cacheName, configurationTemplate);
+            }
+        });
+        Set<String> incorrectConfigurations = new 
HashSet<>(fineractProperties.getCache().getCustomTemplates().keySet());
+        incorrectConfigurations.removeAll(cacheNames);
+        if (!incorrectConfigurations.isEmpty()) {
+            log.warn("The following cache configurations are defined but cache 
does not exists: {}", incorrectConfigurations);
         }
+        return cacheManager;
+    }
 
-        if (cacheManager.getCache("payment_types") == null) {
-            cacheManager.createCache("payment_types", defaultTemplate);
+    private javax.cache.configuration.Configuration<Object, Object> 
generateCustomCacheConfiguration(String cacheIdentifier,
+            javax.cache.configuration.Configuration<Object, Object> 
defaultTemplate, Duration defaultTimeToLive,
+            Integer defaultMaxEntries) {
+        javax.cache.configuration.Configuration<Object, Object> 
configurationTemplate = defaultTemplate;
+        if 
(fineractProperties.getCache().getCustomTemplates().containsKey(cacheIdentifier))
 {
+            Duration timeToLiveExpiration = Objects.requireNonNullElse(
+                    
fineractProperties.getCache().getCustomTemplates().get(cacheIdentifier).getTtl(),
 defaultTimeToLive);
+            Integer maxEntries = Objects.requireNonNullElse(
+                    
fineractProperties.getCache().getCustomTemplates().get(cacheIdentifier).getMaximumEntries(),
 defaultMaxEntries);
+            configurationTemplate = generateCacheConfiguration(maxEntries, 
timeToLiveExpiration);
         }
+        return configurationTemplate;
+    }
 
-        return cacheManager;
+    private static javax.cache.configuration.Configuration<Object, Object> 
generateCacheConfiguration(Integer defaultMaxEntries,
+            Duration defaultTimeToLive) {
+        return 
Eh107Configuration.fromEhcacheCacheConfiguration(CacheConfigurationBuilder
+                .newCacheConfigurationBuilder(Object.class, Object.class, 
ResourcePoolsBuilder.heap(defaultMaxEntries))
+                
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(defaultTimeToLive)).build());
     }
 }
diff --git a/fineract-provider/src/main/resources/application.properties 
b/fineract-provider/src/main/resources/application.properties
index 9bb89cf382..dbc2274ed0 100644
--- a/fineract-provider/src/main/resources/application.properties
+++ b/fineract-provider/src/main/resources/application.properties
@@ -284,6 +284,13 @@ 
fineract.sql-validation.profiles[2].patternRefs[6].name=inject-comment
 fineract.sql-validation.profiles[2].patternRefs[6].order=6
 fineract.sql-validation.profiles[2].enabled=true
 
+#Cache - Default
+fineract.cache.default-template.ttl=1m
+fineract.cache.default-template.maximum-entries=1000
+#Cache - Customization
+fineract.cache.custom-templates.userTFAccessToken.ttl=2h
+fineract.cache.custom-templates.userTFAccessToken.maximum-entries=10000
+
 # Logging pattern for the console
 logging.pattern.console=${CONSOLE_LOG_PATTERN:%clr(%d{yyyy-MM-dd 
HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} 
%clr(%replace([%X{correlationId}]){'\\[\\]', ''}) [%15.15tenantId] 
%clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} 
%clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}}
 logging.pattern.level=%5p 
[${spring.application.name:},%X{traceId:-},%X{spanId:-}]
diff --git a/fineract-provider/src/test/resources/application-test.properties 
b/fineract-provider/src/test/resources/application-test.properties
index 6a6435058a..f9e52bc989 100644
--- a/fineract-provider/src/test/resources/application-test.properties
+++ b/fineract-provider/src/test/resources/application-test.properties
@@ -202,6 +202,10 @@ 
fineract.sql-validation.profiles[2].patternRefs[7].name=inject-comment
 fineract.sql-validation.profiles[2].patternRefs[7].order=7
 fineract.sql-validation.profiles[2].enabled=true
 
+#Cache - Default
+fineract.cache.default-template.ttl=1m
+fineract.cache.default-template.maximum-entries=1000
+
 management.health.jms.enabled=false
 
 # FINERACT 1296

Reply via email to