CacheController.java
/**
* Copyright 2015 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package org.gringlobal.mvc.admin;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.StreamSupport;
import javax.cache.management.CacheStatisticsMXBean;
import javax.management.MBeanServer;
import javax.management.MBeanServerInvocationHandler;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import lombok.extern.slf4j.Slf4j;
import org.gringlobal.api.admin.v1.CacheController.CacheStats;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import com.hazelcast.core.DistributedObject;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import com.hazelcast.map.LocalMapStats;
import com.hazelcast.spring.cache.HazelcastCacheManager;
/**
* Manage caches
*
* @author mobreza
*/
@Controller
@RequestMapping("/admin/cache")
@PreAuthorize("hasAuthority('GROUP_ADMINS')")
@Slf4j
public class CacheController {
@Autowired
private CacheManager cacheManager;
@RequestMapping(method = RequestMethod.POST, value = "/clearCache", params = { "clearAll" })
public String clearCacheAll() {
return clearCaches();
}
@RequestMapping(method = RequestMethod.POST, value = "/clearCaches")
public String clearCaches() {
for (String cacheName : cacheManager.getCacheNames()) {
clearCache(cacheName);
}
return "redirect:/admin/cache/";
}
@RequestMapping(method = RequestMethod.POST, value = "/clearCache")
public String clearCache(@RequestParam(required = true, value = "name") String cacheName) {
final Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
log.info("Clearing cache {}", cacheName);
cache.clear();
} else {
log.info("No such cache: {}", cacheName);
}
return "redirect:/admin/cache/";
}
@RequestMapping(method = RequestMethod.POST, value = "/dump")
public String dumpCache(@RequestParam(required = true, value = "name") String cacheName) {
final Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
log.warn("Inspecting cache {} of type {}", cacheName, cache.getNativeCache().getClass());
var nativeCache = cache.getNativeCache();
if (nativeCache instanceof Map<?,?>) {
((Map<?, ?>) nativeCache).forEach((key, value) -> log.warn("Key={} [{}]: [{}]\n\t={}", Objects.toString(key), key.getClass(), value.getClass(), value));
} else if (nativeCache instanceof javax.cache.Cache){
StreamSupport.stream(((javax.cache.Cache<?, ?>) nativeCache).spliterator(), false)
.filter(Objects::nonNull)
.forEach(entry -> {
var key = Objects.toString(entry.getKey());
var value = entry.getValue();
log.warn("Key={} [{}]: [{}]\n\t={}", key, key.getClass(), value.getClass(), value);
});
} else {
log.warn("Can't dump cache of type: {}", nativeCache.getClass());
}
} else {
log.info("No such cache: {}", cacheName);
}
return "redirect:/admin/cache/";
}
@GetMapping("/")
public String cacheStats(Model model) {
List<CacheStats> cacheMaps = new ArrayList<CacheStats>();
List<Object> cacheOther = new ArrayList<Object>();
if (cacheManager instanceof HazelcastCacheManager) {
cacheStatsForHazelcast(cacheMaps, cacheOther);
} else if (cacheManager instanceof JCacheCacheManager) {
cacheStatsForEhCache(cacheMaps, cacheOther);
}
cacheMaps.sort((a, b) -> a.getName().compareToIgnoreCase(b.getName()));
model.addAttribute("cacheMaps", cacheMaps);
model.addAttribute("cacheOther", cacheOther);
return "/admin/cache";
}
private void cacheStatsForEhCache(List<CacheStats> cacheStatsList, List<Object> cacheOther) {
try {
final MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
final Set<ObjectInstance> cacheBeans = beanServer.queryMBeans(ObjectName.getInstance("javax.cache:type=CacheStatistics,CacheManager=*,Cache=*"), null);
for (ObjectInstance cacheBean : cacheBeans) {
final CacheStatisticsMXBean statistics = MBeanServerInvocationHandler
.newProxyInstance(beanServer, cacheBean.getObjectName(), CacheStatisticsMXBean.class, false);
var cacheStats = new CacheStats();
cacheStats.serviceName = "ehcache";
cacheStats.name = cacheBean.getObjectName().getKeyProperty("Cache");
cacheStats.gets = statistics.getCacheGets();
cacheStats.puts = statistics.getCachePuts();
cacheStats.misses = statistics.getCacheMisses();
cacheStats.hits = statistics.getCacheHits();
cacheStats.hitPercentage = statistics.getCacheHitPercentage();
cacheStats.removals = statistics.getCacheRemovals();
cacheStats.evictions = statistics.getCacheEvictions();
cacheStatsList.add(cacheStats);
}
} catch(Exception e){
log.warn("Exception in cache statistic");
}
}
private void cacheStatsForHazelcast(List<CacheStats> cacheMaps, List<Object> cacheOther) {
Set<HazelcastInstance> instances = Hazelcast.getAllHazelcastInstances();
for (HazelcastInstance hz : instances) {
if (log.isDebugEnabled())
log.debug("\n\nCache stats Instance: {}", hz.getName());
for (DistributedObject o : hz.getDistributedObjects()) {
if (o instanceof IMap) {
IMap<?, ?> imap = (IMap<?, ?>) o;
cacheMaps.add(new CacheStats(imap));
if (log.isDebugEnabled()) {
log.debug("{}: {} {}", imap.getServiceName(), imap.getName(), imap.getPartitionKey());
LocalMapStats localMapStats = imap.getLocalMapStats();
log.debug("created: {}", localMapStats.getCreationTime());
log.debug("owned entries: {}", localMapStats.getOwnedEntryCount());
log.debug("backup entries: {}", localMapStats.getBackupEntryCount());
log.debug("locked entries: {}", localMapStats.getLockedEntryCount());
log.debug("dirty entries: {}", localMapStats.getDirtyEntryCount());
log.debug("hits: {}", localMapStats.getHits());
log.debug("puts: {}", localMapStats.getPutOperationCount());
log.debug("last update: {}", localMapStats.getLastUpdateTime());
log.debug("last access: {}", localMapStats.getLastAccessTime());
}
} else {
if (log.isDebugEnabled())
log.debug("{} {}", o.getClass(), o);
cacheOther.add(o);
}
}
}
}
}