Ehcache for Local Caching in Java

Ehcache is a widely-used, open-source Java caching library that provides in-memory data storage for improving application performance. This comprehensive guide covers Ehcache configuration, usage patterns, and advanced features.

Basic Ehcache Setup

1. Dependencies Configuration

Maven:

<properties>
<ehcache.version>3.10.8</ehcache.version>
</properties>
<dependencies>
<!-- Ehcache Core -->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>${ehcache.version}</version>
</dependency>
<!-- Spring Boot Starter Cache (optional) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- Ehcache with Spring Boot (optional) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-ehcache</artifactId>
</dependency>
</dependencies>

Gradle:

dependencies {
implementation 'org.ehcache:ehcache:3.10.8'
implementation 'org.springframework.boot:spring-boot-starter-cache'
}

2. Basic Cache Configuration

Programmatic Configuration:

import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
public class BasicEhcacheExample {
public static void main(String[] args) {
// Create CacheManager
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.withCache("preConfigured",
CacheConfigurationBuilder.newCacheConfigurationBuilder(
Long.class, String.class, ResourcePoolsBuilder.heap(100))
.build())
.build(true); // true = initialize immediately
// Get or create cache
Cache<Long, String> preConfigured = 
cacheManager.getCache("preConfigured", Long.class, String.class);
Cache<Long, String> myCache = cacheManager.createCache("myCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(
Long.class, String.class, ResourcePoolsBuilder.heap(100))
.build());
// Use cache
myCache.put(1L, "Hello World!");
String value = myCache.get(1L);
System.out.println("Retrieved: " + value);
// Close cache manager
cacheManager.close();
}
}

XML Configuration (ehcache.xml):

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns='http://www.ehcache.org/v3'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xsi:schemaLocation="http://www.ehcache.org/v3
http://www.ehcache.org/schema/ehcache-core-3.10.xsd">
<!-- Default cache template -->
<cache-template name="default">
<key-type>java.lang.Long</key-type>
<value-type>java.lang.String</value-type>
<heap unit="entries">1000</heap>
<expiry>
<tti unit="minutes">30</tti>
</expiry>
</cache-template>
<!-- User cache -->
<cache alias="users" uses-template="default">
<key-type>java.lang.Long</key-type>
<value-type>com.example.User</value-type>
<heap unit="entries">500</heap>
</cache>
<!-- Product cache with different configuration -->
<cache alias="products">
<key-type>java.lang.Long</key-type>
<value-type>com.example.Product</value-type>
<heap unit="entries">2000</heap>
<expiry>
<tti unit="minutes">60</tti>
</expiry>
</cache>
<!-- Large cache with off-heap storage -->
<cache alias="largeData">
<key-type>java.lang.String</key-type>
<value-type>java.lang.String</value-type>
<heap unit="entries">10000</heap>
<offheap unit="MB">50</offheap>
<expiry>
<tti unit="hours">2</tti>
</expiry>
</cache>
</config>

Comprehensive Cache Service Implementation

3. Generic Cache Service

import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.expiry.ExpiryPolicy;
import java.time.Duration;
import java.util.Optional;
import java.util.function.Supplier;
public class CacheService<K, V> {
private final Cache<K, V> cache;
private final String cacheName;
public CacheService(CacheManager cacheManager, String cacheName, 
Class<K> keyType, Class<V> valueType, 
int heapEntries, Duration timeToLive) {
this.cacheName = cacheName;
// Create or get cache
this.cache = cacheManager.getCache(cacheName, keyType, valueType);
if (this.cache == null) {
this.cache = cacheManager.createCache(cacheName,
CacheConfigurationBuilder.newCacheConfigurationBuilder(keyType, valueType,
ResourcePoolsBuilder.heap(heapEntries))
.withExpiry(ExpiryPolicy.builder()
.timeToLiveExpiration(timeToLive)
.build())
.build());
}
}
// Basic operations
public void put(K key, V value) {
cache.put(key, value);
}
public Optional<V> get(K key) {
return Optional.ofNullable(cache.get(key));
}
public boolean contains(K key) {
return cache.containsKey(key);
}
public void remove(K key) {
cache.remove(key);
}
public void clear() {
cache.clear();
}
// Advanced operations
public V getOrCompute(K key, Supplier<V> valueSupplier) {
return get(key).orElseGet(() -> {
V value = valueSupplier.get();
put(key, value);
return value;
});
}
public V getOrComputeWithCondition(K key, Supplier<V> valueSupplier, 
java.util.function.Predicate<V> condition) {
Optional<V> cachedValue = get(key);
if (cachedValue.isPresent() && condition.test(cachedValue.get())) {
return cachedValue.get();
} else {
V value = valueSupplier.get();
put(key, value);
return value;
}
}
public void putIfAbsent(K key, V value) {
if (!contains(key)) {
put(key, value);
}
}
// Bulk operations
public void putAll(java.util.Map<K, V> entries) {
entries.forEach(this::put);
}
public java.util.Map<K, V> getAll(java.util.Set<K> keys) {
return keys.stream()
.collect(java.util.stream.Collectors.toMap(
key -> key,
key -> get(key).orElse(null)
));
}
// Statistics
public long getSize() {
// Note: Ehcache 3 doesn't provide direct size method for performance reasons
// This is a workaround - use with caution in production
return java.util.stream.StreamSupport.stream(
cache.spliterator(), false).count();
}
public String getCacheInfo() {
return String.format("Cache: %s, Estimated Size: %d", cacheName, getSize());
}
}

4. Cache Manager Factory

import org.ehcache.CacheManager;
import org.ehcache.config.Configuration;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.xml.XmlConfiguration;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class CacheManagerFactory {
private static CacheManagerFactory instance;
private final ConcurrentMap<String, CacheManager> cacheManagers;
private final CacheManager defaultCacheManager;
private CacheManagerFactory() {
this.cacheManagers = new ConcurrentHashMap<>();
this.defaultCacheManager = createDefaultCacheManager();
}
public static synchronized CacheManagerFactory getInstance() {
if (instance == null) {
instance = new CacheManagerFactory();
}
return instance;
}
public CacheManager getDefaultCacheManager() {
return defaultCacheManager;
}
public CacheManager getCacheManager(String name) {
return cacheManagers.computeIfAbsent(name, this::createCacheManager);
}
public CacheManager createCacheManagerFromXml(String xmlConfigPath) {
try {
URL xmlConfigUrl = getClass().getResource(xmlConfigPath);
if (xmlConfigUrl == null) {
throw new IllegalArgumentException("XML configuration not found: " + xmlConfigPath);
}
Configuration xmlConfig = new XmlConfiguration(xmlConfigUrl);
return CacheManagerBuilder.newCacheManager(xmlConfig);
} catch (Exception e) {
throw new RuntimeException("Failed to create cache manager from XML", e);
}
}
private CacheManager createDefaultCacheManager() {
return CacheManagerBuilder.newCacheManagerBuilder()
.withCache("defaultCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(
Object.class, Object.class, ResourcePoolsBuilder.heap(1000))
.build())
.build(true);
}
private CacheManager createCacheManager(String name) {
return CacheManagerBuilder.newCacheManagerBuilder()
.withCache(name + "Cache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(
Object.class, Object.class, ResourcePoolsBuilder.heap(500))
.build())
.build(true);
}
public void shutdown() {
cacheManagers.values().forEach(CacheManager::close);
defaultCacheManager.close();
cacheManagers.clear();
}
public void shutdown(String cacheManagerName) {
CacheManager cacheManager = cacheManagers.remove(cacheManagerName);
if (cacheManager != null) {
cacheManager.close();
}
}
}

Domain-Specific Cache Services

5. User Cache Service

import java.time.Duration;
import java.util.List;
import java.util.Optional;
public class UserCacheService {
private final CacheService<Long, User> userCache;
private final CacheService<String, List<User>> userSearchCache;
private final CacheService<Long, List<Order>> userOrdersCache;
public UserCacheService(CacheManager cacheManager) {
this.userCache = new CacheService<>(
cacheManager, "users", Long.class, User.class, 1000, Duration.ofMinutes(30));
this.userSearchCache = new CacheService<>(
cacheManager, "userSearch", String.class, (Class<List<User>>) (Class<?>) List.class, 
500, Duration.ofMinutes(10));
this.userOrdersCache = new CacheService<>(
cacheManager, "userOrders", Long.class, (Class<List<Order>>) (Class<?>) List.class,
2000, Duration.ofMinutes(15));
}
// User operations
public Optional<User> getUser(Long userId) {
return userCache.get(userId);
}
public void cacheUser(User user) {
userCache.put(user.getId(), user);
}
public void evictUser(Long userId) {
userCache.remove(userId);
userOrdersCache.remove(userId); // Also evict related orders
}
// Search operations
public Optional<List<User>> getUsersBySearch(String searchTerm) {
return userSearchCache.get(generateSearchKey(searchTerm));
}
public void cacheUserSearch(String searchTerm, List<User> users) {
userSearchCache.put(generateSearchKey(searchTerm), users);
}
public void evictAllSearches() {
// In real implementation, you might want more selective eviction
userSearchCache.clear();
}
// Order operations
public Optional<List<Order>> getUserOrders(Long userId) {
return userOrdersCache.get(userId);
}
public void cacheUserOrders(Long userId, List<Order> orders) {
userOrdersCache.put(userId, orders);
}
public void evictUserOrders(Long userId) {
userOrdersCache.remove(userId);
}
// Bulk operations
public void cacheAllUsers(List<User> users) {
users.forEach(this::cacheUser);
}
public void evictAllUserData() {
userCache.clear();
userSearchCache.clear();
userOrdersCache.clear();
}
// Helper methods
private String generateSearchKey(String searchTerm) {
return "search:" + searchTerm.toLowerCase().trim();
}
// Statistics
public void printCacheStats() {
System.out.println("User Cache: " + userCache.getCacheInfo());
System.out.println("Search Cache: " + userSearchCache.getCacheInfo());
System.out.println("Orders Cache: " + userOrdersCache.getCacheInfo());
}
}

6. Product Cache Service with Advanced Features

import org.ehcache.event.CacheEvent;
import org.ehcache.event.CacheEventListener;
import org.ehcache.event.EventType;
public class ProductCacheService {
private final CacheService<Long, Product> productCache;
private final CacheService<String, List<Product>> categoryCache;
private final CacheService<String, PaginatedResult<Product>> searchCache;
public ProductCacheService(CacheManager cacheManager) {
this.productCache = new CacheService<>(
cacheManager, "products", Long.class, Product.class, 5000, Duration.ofHours(1));
this.categoryCache = new CacheService<>(
cacheManager, "productCategories", String.class, (Class<List<Product>>) (Class<?>) List.class,
100, Duration.ofMinutes(30));
this.searchCache = new CacheService<>(
cacheManager, "productSearch", String.class, 
(Class<PaginatedResult<Product>>) (Class<?>) PaginatedResult.class,
200, Duration.ofMinutes(15));
setupEventListeners();
}
// Product operations
public Optional<Product> getProduct(Long productId) {
return productCache.get(productId);
}
public Product getProductOrLoad(Long productId, Supplier<Product> loader) {
return productCache.getOrCompute(productId, loader);
}
public void cacheProduct(Product product) {
productCache.put(product.getId(), product);
// Evict category cache when product is updated
evictCategoryCache(product.getCategory());
}
public void evictProduct(Long productId) {
Optional<Product> product = getProduct(productId);
productCache.remove(productId);
// Evict related caches
product.ifPresent(p -> {
evictCategoryCache(p.getCategory());
evictAllSearchCaches();
});
}
// Category operations
public Optional<List<Product>> getProductsByCategory(String category) {
return categoryCache.get(normalizeCategoryKey(category));
}
public void cacheProductsByCategory(String category, List<Product> products) {
categoryCache.put(normalizeCategoryKey(category), products);
}
public void evictCategoryCache(String category) {
categoryCache.remove(normalizeCategoryKey(category));
}
// Search operations
public Optional<PaginatedResult<Product>> getSearchResults(String query, int page, int size) {
return searchCache.get(generateSearchKey(query, page, size));
}
public void cacheSearchResults(String query, int page, int size, PaginatedResult<Product> results) {
searchCache.put(generateSearchKey(query, page, size), results);
}
public void evictAllSearchCaches() {
searchCache.clear();
}
// Price-based caching
public void cacheProductsWithPriceRange(String category, double minPrice, double maxPrice, 
List<Product> products) {
String key = String.format("price_range:%s:%.2f-%.2f", category, minPrice, maxPrice);
categoryCache.put(key, products);
}
public Optional<List<Product>> getProductsByPriceRange(String category, double minPrice, double maxPrice) {
String key = String.format("price_range:%s:%.2f-%.2f", category, minPrice, maxPrice);
return categoryCache.get(key);
}
// Bulk operations
public void cacheAllProducts(List<Product> products) {
products.forEach(this::cacheProduct);
// Group by category and cache
products.stream()
.collect(java.util.stream.Collectors.groupingBy(Product::getCategory))
.forEach(this::cacheProductsByCategory);
}
public void evictAllProductData() {
productCache.clear();
categoryCache.clear();
searchCache.clear();
}
// Event listeners for cache monitoring
private void setupEventListeners() {
// In Ehcache 3, event listeners are configured at cache creation time
// This is a simplified example
}
// Helper methods
private String normalizeCategoryKey(String category) {
return "category:" + category.toLowerCase().trim();
}
private String generateSearchKey(String query, int page, int size) {
return String.format("search:%s:%d:%d", 
query.toLowerCase().trim(), page, size);
}
// Cache warming
public void warmUpCache(Supplier<List<Product>> productLoader) {
System.out.println("Warming up product cache...");
List<Product> products = productLoader.get();
cacheAllProducts(products);
System.out.println("Cache warm-up completed. Loaded " + products.size() + " products.");
}
}

Spring Boot Integration

7. Spring Cache Configuration

@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager ehcacheCacheManager() {
return new EhCacheCacheManager(ehCacheManager());
}
@Bean(destroyMethod = "close")
public CacheManager ehCacheManager() {
try {
URL xmlConfigUrl = getClass().getResource("/ehcache.xml");
Configuration configuration = new XmlConfiguration(xmlConfigUrl);
return CacheManagerBuilder.newCacheManager(configuration);
} catch (Exception e) {
// Fallback to programmatic configuration
return CacheManagerBuilder.newCacheManagerBuilder()
.withCache("users",
CacheConfigurationBuilder.newCacheConfigurationBuilder(
Long.class, User.class, ResourcePoolsBuilder.heap(1000))
.withExpiry(ExpiryPolicy.builder().timeToLiveExpiration(Duration.ofMinutes(30)).build())
.build())
.withCache("products",
CacheConfigurationBuilder.newCacheConfigurationBuilder(
Long.class, Product.class, ResourcePoolsBuilder.heap(5000))
.withExpiry(ExpiryPolicy.builder().timeToLiveExpiration(Duration.ofHours(1)).build())
.build())
.build(true);
}
}
@Bean
public UserCacheService userCacheService() {
return new UserCacheService(ehCacheManager());
}
@Bean
public ProductCacheService productCacheService() {
return new ProductCacheService(ehCacheManager());
}
}

8. Spring Service with Caching

@Service
@Transactional
public class CachedUserService {
private final UserRepository userRepository;
private final OrderRepository orderRepository;
private final UserCacheService userCacheService;
public CachedUserService(UserRepository userRepository,
OrderRepository orderRepository,
UserCacheService userCacheService) {
this.userRepository = userRepository;
this.orderRepository = orderRepository;
this.userCacheService = userCacheService;
}
@Cacheable(value = "users", key = "#userId")
public User getUserById(Long userId) {
System.out.println("Fetching user from database: " + userId);
return userRepository.findById(userId)
.orElseThrow(() -> new UserNotFoundException("User not found: " + userId));
}
@CacheEvict(value = "users", key = "#user.id")
public User updateUser(User user) {
User updatedUser = userRepository.save(user);
userCacheService.evictUser(user.getId()); // Evict custom cache
return updatedUser;
}
@CacheEvict(value = "users", key = "#userId")
public void deleteUser(Long userId) {
userRepository.deleteById(userId);
userCacheService.evictUser(userId);
}
// Custom caching with cache service
public List<User> searchUsers(String searchTerm) {
// Try cache first
Optional<List<User>> cachedResult = userCacheService.getUsersBySearch(searchTerm);
if (cachedResult.isPresent()) {
System.out.println("Returning cached search results for: " + searchTerm);
return cachedResult.get();
}
// Fetch from database
System.out.println("Searching users in database: " + searchTerm);
List<User> users = userRepository.findBySearchTerm(searchTerm);
// Cache the results
userCacheService.cacheUserSearch(searchTerm, users);
return users;
}
public List<Order> getUserOrders(Long userId) {
// Try cache first
Optional<List<Order>> cachedOrders = userCacheService.getUserOrders(userId);
if (cachedOrders.isPresent()) {
return cachedOrders.get();
}
// Fetch from database
List<Order> orders = orderRepository.findByUserId(userId);
// Cache the results
userCacheService.cacheUserOrders(userId, orders);
return orders;
}
// Cache warming on application startup
@EventListener(ApplicationReadyEvent.class)
public void warmUpCaches() {
System.out.println("Warming up user caches...");
List<User> activeUsers = userRepository.findActiveUsers();
userCacheService.cacheAllUsers(activeUsers);
System.out.println("User cache warm-up completed.");
}
}

Advanced Ehcache Features

9. Cache Event Listeners

import org.ehcache.event.CacheEvent;
import org.ehcache.event.CacheEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CacheEventLogger implements CacheEventListener<Object, Object> {
private static final Logger logger = LoggerFactory.getLogger(CacheEventLogger.class);
@Override
public void onEvent(CacheEvent<? extends Object, ? extends Object> event) {
switch (event.getType()) {
case CREATED:
logger.info("Cache entry created - Key: {}, Value: {}", 
event.getKey(), event.getNewValue());
break;
case UPDATED:
logger.info("Cache entry updated - Key: {}, Old: {}, New: {}", 
event.getKey(), event.getOldValue(), event.getNewValue());
break;
case EVICTED:
logger.info("Cache entry evicted - Key: {}, Value: {}", 
event.getKey(), event.getOldValue());
break;
case EXPIRED:
logger.info("Cache entry expired - Key: {}, Value: {}", 
event.getKey(), event.getOldValue());
break;
case REMOVED:
logger.info("Cache entry removed - Key: {}, Value: {}", 
event.getKey(), event.getOldValue());
break;
}
}
}
// Configuration with event listener
public class CacheWithEventListener {
public CacheManager createCacheManagerWithListener() {
return CacheManagerBuilder.newCacheManagerBuilder()
.withCache("cacheWithListener",
CacheConfigurationBuilder.newCacheConfigurationBuilder(
String.class, String.class, ResourcePoolsBuilder.heap(100))
.withService(CacheEventListenerConfigurationBuilder
.newEventListenerConfiguration(
new CacheEventLogger(), EventType.CREATED, EventType.UPDATED, EventType.REMOVED)
.unordered().asynchronous())
.build())
.build(true);
}
}

10. Multi-Tier Caching

public class MultiTierCacheExample {
public CacheManager createMultiTierCacheManager() {
return CacheManagerBuilder.newCacheManagerBuilder()
.withCache("threeTierCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(
Long.class, String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(1000, org.ehcache.config.units.MemoryUnit.ENTRIES) // First level: Heap
.offheap(10, org.ehcache.config.units.MemoryUnit.MB)     // Second level: Off-heap
.disk(100, org.ehcache.config.units.MemoryUnit.MB, true) // Third level: Disk (persistent)
)
.withExpiry(ExpiryPolicy.builder().timeToLiveExpiration(Duration.ofHours(1)).build())
.build())
.build(true);
}
public CacheManager createCacheWithSizeOfEngine() {
return CacheManagerBuilder.newCacheManagerBuilder()
.withDefaultSizeOfMaxObjectSize(500, org.ehcache.config.units.MemoryUnit.KB)
.withDefaultSizeOfMaxObjectGraph(2000)
.withCache("sizedCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(
Long.class, byte[].class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, org.ehcache.config.units.MemoryUnit.MB)
.offheap(50, org.ehcache.config.units.MemoryUnit.MB)
)
.withSizeOfMaxObjectSize(1, org.ehcache.config.units.MemoryUnit.MB)
.withSizeOfMaxObjectGraph(1000)
.build())
.build(true);
}
}

Testing Cache Implementations

11. Cache Testing

@ExtendWith(MockitoExtension.class)
class UserCacheServiceTest {
private CacheManager cacheManager;
private UserCacheService userCacheService;
@BeforeEach
void setUp() {
cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build(true);
userCacheService = new UserCacheService(cacheManager);
}
@AfterEach
void tearDown() {
cacheManager.close();
}
@Test
void cacheUser_ThenRetrieve_ShouldReturnCachedUser() {
// Arrange
User user = new User(1L, "[email protected]", "John Doe");
// Act
userCacheService.cacheUser(user);
Optional<User> cachedUser = userCacheService.getUser(1L);
// Assert
assertTrue(cachedUser.isPresent());
assertEquals(user.getId(), cachedUser.get().getId());
assertEquals(user.getEmail(), cachedUser.get().getEmail());
}
@Test
void evictUser_ThenRetrieve_ShouldReturnEmpty() {
// Arrange
User user = new User(1L, "[email protected]", "John Doe");
userCacheService.cacheUser(user);
// Act
userCacheService.evictUser(1L);
Optional<User> cachedUser = userCacheService.getUser(1L);
// Assert
assertFalse(cachedUser.isPresent());
}
@Test
void cacheUserSearch_ThenRetrieve_ShouldReturnCachedResults() {
// Arrange
List<User> users = List.of(
new User(1L, "[email protected]", "John Doe"),
new User(2L, "[email protected]", "Jane Smith")
);
// Act
userCacheService.cacheUserSearch("john", users);
Optional<List<User>> cachedResults = userCacheService.getUsersBySearch("john");
// Assert
assertTrue(cachedResults.isPresent());
assertEquals(2, cachedResults.get().size());
}
}
@SpringBootTest
class CachedUserServiceIntegrationTest {
@Autowired
private CachedUserService cachedUserService;
@Autowired
private UserRepository userRepository;
@Test
void getUserById_WithCaching_ShouldHitCacheOnSecondCall() {
// Arrange
User user = new User(1L, "[email protected]", "Test User");
userRepository.save(user);
// Act - First call (should hit database)
User firstResult = cachedUserService.getUserById(1L);
// Act - Second call (should hit cache)
User secondResult = cachedUserService.getUserById(1L);
// Assert
assertEquals(user.getId(), firstResult.getId());
assertEquals(user.getId(), secondResult.getId());
// Verify caching by checking logs or using mock
}
}

Performance Monitoring and Management

12. Cache Statistics and Monitoring

public class CacheMonitor {
private final CacheManager cacheManager;
private final ScheduledExecutorService scheduler;
public CacheMonitor(CacheManager cacheManager) {
this.cacheManager = cacheManager;
this.scheduler = Executors.newScheduledThreadPool(1);
}
public void startMonitoring() {
scheduler.scheduleAtFixedRate(this::logCacheStats, 0, 5, TimeUnit.MINUTES);
}
public void stopMonitoring() {
scheduler.shutdown();
}
private void logCacheStats() {
cacheManager.getRuntimeConfiguration().getCacheConfigurations().forEach((cacheName, config) -> {
Cache<?, ?> cache = cacheManager.getCache(cacheName, config.getKeyType(), config.getValueType());
if (cache != null) {
// Note: Ehcache 3 doesn't provide built-in statistics in open source version
// Enterprise version provides StatisticsService
System.out.printf("Cache: %s, Estimated Size: %d%n", 
cacheName, estimateCacheSize(cache));
}
});
}
private long estimateCacheSize(Cache<?, ?> cache) {
return java.util.stream.StreamSupport.stream(
cache.spliterator(), false).count();
}
public void printDetailedCacheInfo() {
System.out.println("=== Cache Manager Information ===");
System.out.println("Cache Manager: " + cacheManager.toString());
cacheManager.getRuntimeConfiguration().getCacheConfigurations().forEach((name, config) -> {
System.out.printf("Cache: %s%n", name);
System.out.printf("  Key Type: %s%n", config.getKeyType().getSimpleName());
System.out.printf("  Value Type: %s%n", config.getValueType().getSimpleName());
System.out.printf("  Resource Pool: %s%n", config.getResourcePools().getPoolForResource(ResourceType.Core.HEAP));
});
}
}

Key Benefits of Ehcache

  1. High Performance: In-memory caching for fast data access
  2. Flexible Configuration: Multiple storage tiers (heap, off-heap, disk)
  3. Spring Integration: Seamless integration with Spring Framework
  4. Enterprise Features: Clustering, persistence, and monitoring in enterprise version
  5. Memory Management: Automatic eviction policies and size-based constraints
  6. Statistics: Built-in monitoring and statistics (enterprise version)
  7. Standards Compliance: JSR-107 (JCache) compliant

Ehcache provides a robust, feature-rich caching solution suitable for both simple and complex caching requirements in Java applications.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper