CacheController.java

/*
 * Copyright 2021 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.genesys.server.mvc.admin;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.genesys.server.service.MappingService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
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;

/**
 * Manage caches
 * 
 * @author mobreza
 */
@Controller
@RequestMapping("/admin/cache")
@PreAuthorize("hasRole('ADMINISTRATOR')")
public class CacheController {

	public static final Logger LOG = LoggerFactory.getLogger(AdminController.class);

	@Autowired
	private MappingService mappingService;

	@Autowired
	private CacheManager cacheManager;

	@RequestMapping(method = RequestMethod.POST, value = "/clearCache", params = { "clearTiles" })
	public String clearCacheTiles() {
		return clearTilesCache();
	}

	@RequestMapping(method = RequestMethod.POST, value = "/clearCache", params = { "clearAll" })
	public String clearCacheAll() {
		return clearCaches();
	}

	@RequestMapping(method = RequestMethod.POST, value = "/clearTilesCache")
	public String clearTilesCache() {
		final Cache tileServerCache = cacheManager.getCache("tileserver");
		if (LOG.isDebugEnabled()) {
			LOG.debug("tileServerCache={}", tileServerCache.getNativeCache());
		}

		@SuppressWarnings("rawtypes")
		final IMap hazelCache = (IMap) tileServerCache.getNativeCache();

		LOG.info("Tiles cache size={}", hazelCache.size());
		int count = 0;
		for (final Object key : hazelCache.keySet()) {
			LOG.info("\tkey={}", key);
			if (++count > 20) {
				break;
			}
		}
		mappingService.clearCache();
		LOG.info("Tiles cache size={}", hazelCache.size());

		return "redirect:/admin/cache/";
	}

	@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("/")
	public String cacheStats(Model model) {
		List<CacheStats> cacheMaps = new ArrayList<CacheStats>();
		List<Object> cacheOther = new ArrayList<Object>();

		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);
				}
			}
		}

		cacheMaps.sort((a, b) -> a.name.compareTo(b.name));
		model.addAttribute("cacheMaps", cacheMaps);
		model.addAttribute("cacheOther", cacheOther);

		return "/admin/cache";
	}

	public static final class CacheStats {

		private String serviceName;
		private String name;
		private LocalMapStats mapStats;

		public CacheStats(IMap<?, ?> imap) {
			this.serviceName = imap.getServiceName();
			this.name = imap.getName();
			this.mapStats = imap.getLocalMapStats();
		}

		public String getServiceName() {
			return serviceName;
		}

		public String getName() {
			return name;
		}

		public LocalMapStats getMapStats() {
			return mapStats;
		}
	}

}