diff --git a/.gitignore b/.gitignore index ace4de0..d695e69 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ hs_err_pid* *~ \#*\# \.\# +/.project diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..99f26c0 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/README.md b/README.md index 6c2a5d9..e40f878 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ # Java Lessons 1) [Lesson #1](springboot2-junit5-skiptest/README.md) - How to write an annotation for your JUnit5 tests and to use application.properties from Spring environment -2) [Lesson #2](springboot2-request-logger/README.md) - How to log every request in Spring/SpringBoot web application \ No newline at end of file + +2) [Lesson #2](springboot2-request-logger/README.md) - How to log every request in Spring/SpringBoot web application + +# Snippets + +1) [Flexible Caffeine Cache Manager](flexible-caffeine-cache/FlexibleCaffeineCacheManager.java) - How to separate different cache configurations into one application using Caffeine cache \ No newline at end of file diff --git a/flexible-caffeine-cache/FlexibleCaffeineCacheManager.java b/flexible-caffeine-cache/FlexibleCaffeineCacheManager.java new file mode 100644 index 0000000..1a5b9a9 --- /dev/null +++ b/flexible-caffeine-cache/FlexibleCaffeineCacheManager.java @@ -0,0 +1,98 @@ +package com.pf.core.cache; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.CacheLoader; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.CaffeineSpec; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cache.caffeine.CaffeineCacheManager; +import org.springframework.util.StringUtils; + +import javax.annotation.Nonnull; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** Flexible Caffeine cache manager + * Idea: {@see "https://dzone.com/articles/multiple-cache-configurations-with-caffeine-and-sp"} + * + * to be used in cases when you need different settings per cache name + * + */ +@Slf4j +@ConfigurationProperties(prefix = "cache") +public class FlexibleCaffeineCacheManager extends CaffeineCacheManager implements InitializingBean { + + /** + * default spring.cache.cache-names settings + * will be overridden with {@link FlexibleCaffeineCacheManager#specs} + */ + @Value("${spring.cache.cache-names:}") + private String springCacheNames; + + /** + * default spring.cache.caffeine.spec settings + * will be overridden with {@link FlexibleCaffeineCacheManager#specs} + */ + @Value("${spring.cache.caffeine.spec:}") + private String springCacheSpecs; + + /** + * This field could be populated from application.(yaml|properties) file + * All features are listed into {@link CaffeineSpec} + */ + @Getter @Setter + private Map specs = new ConcurrentHashMap<>(); + @Getter + private Map> builders = new ConcurrentHashMap<>(); + + private CacheLoader cacheLoader; + @Override + public void setCacheLoader(@Nonnull CacheLoader cacheLoader) { + this.cacheLoader = cacheLoader; + } + + /** + * reads all standard settings from application.(properties|yml) and adjust proper cache builders + * @throws Exception + */ + @Override + public void afterPropertiesSet() throws Exception { + if (StringUtils.hasText(springCacheNames) && !springCacheNames.equals("${spring.cache.cache-names:}")) { + String[] cacheNames = springCacheNames.split(","); + if (cacheNames.length > 0) { + for (String cacheName : cacheNames) { + if (StringUtils.hasText(cacheName)) { + if (!specs.containsKey(cacheName.trim())) { + specs.put(cacheName.trim(), springCacheSpecs); + } + } + } + } + } + for (Map.Entry cacheSpecEntry : specs.entrySet()) { + log.info("Creating cache with name {} and specs: {}", cacheSpecEntry.getKey(), cacheSpecEntry.getValue()); + builders.put(cacheSpecEntry.getKey(), Caffeine.from(cacheSpecEntry.getValue())); + } + } + + @Nonnull + @Override + protected Cache createNativeCaffeineCache(String name) { + Caffeine builder = builders.get(name); + if (builder == null) { + return super.createNativeCaffeineCache(name); + } + + if (this.cacheLoader != null) { + return builder.build(this.cacheLoader); + } else { + return builder.build(); + } + } + +}