package com.kendelong.util.ehcache; import java.lang.reflect.Method; import java.text.NumberFormat; import java.util.Date; import java.util.Iterator; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jmx.export.annotation.ManagedAttribute; import org.springframework.jmx.export.annotation.ManagedOperation; import org.springframework.jmx.export.annotation.ManagedOperationParameter; import org.springframework.jmx.export.annotation.ManagedOperationParameters; import org.springframework.jmx.export.annotation.ManagedResource; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Ehcache; import net.sf.ehcache.Element; /** * Allows one to use JMX to examine the contents of the Ehcaches in the JVM. Meant for display in an HTML page. * * @author Ken */ @ManagedResource(objectName="net.sf.ehcache:name=CacheContentsExaminer", description="Looking inside the EhCaches") public class EhcacheExaminer { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @ManagedOperation(description="Show the keys for the items in the given cache") @ManagedOperationParameters ({ @ManagedOperationParameter(name="cacheManagerName", description="Name of the cache manager (pick from above list)"), @ManagedOperationParameter(name="cacheName", description="Name of the cache (pick from above list)") }) public String listKeysFor(String cacheManagerName, String cacheName) { List<?> keys = null; Ehcache cache = findCache(cacheManagerName, cacheName); if(cache == null) { logger.warn("Could not find cache [{}] in cacheManager [{}]", cacheName, cacheManagerName); return "Cache not found"; } keys = cache.getKeys(); StringBuilder keyNames = new StringBuilder(); if(keys != null) { for(Object key : keys) { String s = key.toString(); keyNames.append(s + "\n"); } } return keyNames.toString(); } @ManagedAttribute(description="The cache managers and (indented) their associated caches.") public String getAllCacheManagers() { StringBuilder names = new StringBuilder(); for(CacheManager cacheManager: CacheManager.ALL_CACHE_MANAGERS) { names.append("<P>" + cacheManager.getName() + "<BR/>"); String[] cacheNames = cacheManager.getCacheNames(); for(String cacheName : cacheNames) { names.append(" - " + cacheName + "<BR/>"); } } return names.toString(); } @ManagedOperation(description="This will show the size of each cache, but it does so by serializing the content" + "and measuring the size of the resulting byte array. For a full cache, this method could take a minute" + "or so of processing, so never call it in prod unless it's a low traffic time") public String showCacheMemorySizesDontUseInProd() { long total = 0; String format = "%,9d"; StringBuilder sb = new StringBuilder(); sb.append("<script src=\"/js/sorttable.js\"></script>"); sb.append("<script src=\"/webassets/js/sorttable.js\"></script>"); sb.append("<table border=\"2\" class=\"sortable\"><tbody><tr><th>Cache Name</th><th>Manager</th><th>Number</th><th>Cache Size</th></tr>"); for(CacheManager cacheManager: CacheManager.ALL_CACHE_MANAGERS) { String managerName = cacheManager.getName(); String[] cacheNames = cacheManager.getCacheNames(); for(int i = 0; i < cacheNames.length; i++) { String cacheName = cacheNames[i]; Ehcache cache = cacheManager.getEhcache(cacheName); int numElements = cache.getSize(); long size = cache.calculateInMemorySize(); String s = String.format(format, size); sb.append("<tr><td>").append(cacheName).append("</td><td>").append(managerName) .append("</td><td>").append(numElements) .append("</td><td style=\"text-align: right;\">").append(s).append("</td></tr>"); total += size; } } sb.append("</tbody></table>"); sb.append("\nTotal: ").append(NumberFormat.getIntegerInstance().format(total)); sb.append(" ").append("bytes"); return sb.toString(); } @ManagedOperation(description="Show the item in the cache for the key") @ManagedOperationParameters ({ @ManagedOperationParameter(name="cacheManagerName", description="Name of the cache manager (pick from above list)"), @ManagedOperationParameter(name="cacheName", description="Name of the cache (pick from above list)"), @ManagedOperationParameter(name="key", description="The key for the item (use listKeysFor() above)") }) public String examineCacheContentsWithStringKey(String cacheManagerName, String cacheName, String keyName) { Element element = null; Ehcache cache = findCache(cacheManagerName, cacheName); if(cache == null) { return "Cache " + cacheName + " not found."; } // Keys are not always Strings!! So we have to use this wonky way of locating the actual key we want @SuppressWarnings("unchecked") Object key = cache.getKeys().stream().filter(k -> keyName.equals(k.toString())).findFirst().orElse(null); if(key != null) { element = cache.get(key); } else { return "Key [" + keyName + "] could not be located"; } if(element == null) return "null object returned from cache"; StringBuilder builder = new StringBuilder(); builder.append("Creation time: ").append(new Date(element.getCreationTime())).append("<br/>") .append("Last access time: ").append(new Date(element.getLastAccessTime())).append("<br/>") .append("Expiration time: ").append(new Date(element.getExpirationTime())).append("<br/>") .append("value: ").append(element.getObjectValue().toString()).append("<br/>"); try { // When used with hibernate (in 4.3.11, anyway) the values are wrapped in AbstractReadWriteEhcacheAccessStrategy$Item classed // Those hold CacheEntries, and so on and so on. This helps get out a litte more information Object value = element.getObjectValue(); Method getValueMethod = value.getClass().getMethod("getValue", new Class<?>[]{} ); Object o = getValueMethod.invoke(value, new Object[]{}); builder.append("Inner value: ").append(o.toString()).append("<br/>"); } catch(Exception e) { logger.warn("Problem unwrapping Ehcache elements: " + e.getMessage()); } return builder.toString(); } private Ehcache findCache(String cacheManagerName, String cacheName) { Ehcache cache = null; List<CacheManager> cacheManagers = CacheManager.ALL_CACHE_MANAGERS; Iterator<CacheManager> iter = cacheManagers.iterator(); while(iter.hasNext()) { CacheManager cacheManager = (CacheManager) iter.next(); if(cacheManager.getName().equals(cacheManagerName)) { cache = cacheManager.getEhcache(cacheName); break; } } return cache; } @ManagedOperation(description="This will loop through all the caches attached to all the cache managers on this" + "machine, and call cache.removeAll() on each cache. Cache.removeAll() calls are broadcast throughout the " + "cluster, so this button should clear all caches in all machings in the cluster. Use with caution: this means " + "every server will be pummelling the database to get fresh data at the same time.") public void clearAllCaches() { for(CacheManager cacheManager: CacheManager.ALL_CACHE_MANAGERS) { for(String cacheName : cacheManager.getCacheNames()) { Ehcache cache = cacheManager.getEhcache(cacheName); cache.removeAll(); } } } }