001/* 002 * JKNIV, utils - Helper utilities for jdk code. 003 * 004 * Copyright (C) 2017, the original author or authors. 005 * 006 * This library is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 2.1 of the License. 010 * 011 * This library is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with this library; if not, write to the Free Software Foundation, Inc., 018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 019 */ 020package net.sf.jkniv.cache; 021 022import java.util.HashMap; 023import java.util.Map; 024import java.util.Set; 025import java.util.concurrent.Executors; 026import java.util.concurrent.ScheduledExecutorService; 027import java.util.concurrent.ScheduledFuture; 028import java.util.concurrent.TimeUnit; 029 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033/** 034 * Manager the live of cache entries. 035 * 036 * To enable logging for cache configure logger category: 037 * 038 * <pre> 039 * <logger name="jkniv.utils.CACHE" additivity="false"> 040 * <level value="debug" /> 041 * <appender-ref ref="console" /> 042 * </logger> 043 * </pre> 044 * 045 * @author Alisson Gomes 046 * @since 0.6.0 047 */ 048public class CacheManager<K, V> 049{ 050 private static final Logger LOG = LoggerFactory.getLogger("jkniv.utils.CACHE"); 051 private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); 052 private final Map<String, Cacheable<K, V>> caches; 053 private final Map<String, CachePolicy> policies; 054 private ScheduledFuture<?> poolScheduler; 055 private final long delay; 056 private final long period; 057 private final CachePolicy policy; 058 059 /** 060 * Create a new cache manager with 5 seconds for initial delay to first execution 061 * and 5 minutes for period between successive executions. 062 */ 063 public CacheManager() 064 { 065 this(5L, TimeUnit.MINUTES.toSeconds(5L)); 066 } 067 068 /** 069 * Create a new cache manager with 5 seconds for initial delay to first execution 070 * and configurable parameter for {@code period} to between successive executions. 071 * @param period the period between successive executions 072 */ 073 public CacheManager(long period) 074 { 075 this(5L, period); 076 } 077 078 /** 079 * Create a new cache manager with configurable {@code initialDelay} and {@code period}. 080 * @param initialDelay seconds to the time to delay first execution 081 * @param period seconds to the period between successive executions 082 */ 083 public CacheManager(long initialDelay, long period) 084 { 085 this(initialDelay, period, 086 new TTLCachePolicy(CachePolicy.DEFAULT_TTL, CachePolicy.DEFAULT_TTI, TimeUnit.MINUTES)); 087 } 088 089 /** 090 * Create a new cache manager with configurable {@code initialDelay} and {@code period}. 091 * @param delay seconds to the time to delay first execution 092 * @param period seconds to the period between successive executions 093 * @param policy default policy for cache entries {@link #add(String, Cacheable)} 094 */ 095 public CacheManager(long delay, long period, CachePolicy policy) 096 { 097 this.delay = delay; 098 this.period = period; 099 this.caches = new HashMap<String, Cacheable<K, V>>(); 100 this.policies = new HashMap<String, CachePolicy>(); 101 this.policy = policy; 102 } 103 104 /** 105 * Add new policy with specific name 106 * @param policyName name of policy 107 * @param policy policy to manager a cache 108 * @return the previous value associated with <code>policyName</code>, 109 * or <code>null</code> if there was no mapping for <code>policyName</code> 110 */ 111 public CachePolicy add(String policyName, CachePolicy policy) 112 { 113 return this.policies.put(policyName, policy); 114 } 115 116 /** 117 * Add a new cache for manager 118 * @param key cache identify 119 * @param cache the managed cache 120 * @return the previous value associated with <code>key</code>, 121 * or <code>null</code> if there was no mapping for <code>key</code> 122 */ 123 public Cacheable<K, V> add(String key, Cacheable<K, V> cache) 124 { 125 return this.caches.put(key, cache); 126 } 127 128 /** 129 * Add a new cache for manager 130 * @param key cache identify 131 * @param policyName name of policy, the cache no exist a default cache is associated 132 * @param cache the managed cache 133 * @return the previous value associated with <code>key</code>, 134 * or <code>null</code> if there was no mapping for <code>key</code> 135 */ 136 public Cacheable<K, V> add(String key, String policyName, Cacheable<K, V> cache) 137 { 138 CachePolicy policy = this.policies.get(policyName); 139 if (policy == null) 140 { 141 LOG.warn("There is no a cache policy named [{}] associating default cache for [{}]", policyName, key); 142 policy = this.policy; 143 } 144 cache.setPolicy(policy); 145 return this.caches.put(key, cache); 146 } 147 148 /** 149 * Create a new Memory cache to be managed with a default policy 150 * @param key cache identify 151 * @return the new Cache created 152 */ 153 public Cacheable<K, V> add(String key) 154 { 155 Cacheable<K, V> cacheable = new MemoryCache<K, V>(this.policy, key); 156 this.caches.put(key, cacheable); 157 return cacheable; 158 } 159 160 public void pooling() 161 { 162 //[jkniv-cache-man] 163 final Runnable watching = new WatchCaches(caches); 164 this.poolScheduler = scheduler.scheduleAtFixedRate(watching, this.delay, this.period, TimeUnit.SECONDS); 165 LOG.info("Watching cacheable data for [{}] caches", size()); 166 } 167 168 public void cancel() 169 { 170 LOG.info("Canceling cacheable manager"); 171 this.clear(); 172 if (this.poolScheduler != null) 173 this.poolScheduler.cancel(true); 174 } 175 176 private void clear() 177 { 178 Set<Map.Entry<String, Cacheable<K, V>>> entries = caches.entrySet(); 179 for (Map.Entry<String, Cacheable<K, V>> entry : entries) 180 { 181 LOG.info("Cache [{}] has {} objects removed", entry.getValue().getName(), entry.getValue().size()); 182 Cacheable<K, V> cacheable = entry.getValue(); 183 cacheable.clear(); 184 } 185 caches.clear(); 186 } 187 188 /** 189 * Returns the total of elements in all cache entries in this manager. 190 * @return the sum of all cache elements in this manager 191 */ 192 public long total() 193 { 194 long size = 0L; 195 for (Map.Entry<String, Cacheable<K, V>> entry : caches.entrySet()) 196 { 197 size += entry.getValue().size(); 198 } 199 return size; 200 } 201 202 /** 203 * Returns the number of caches in this manager 204 * @return the number of cache. 205 */ 206 public int size() 207 { 208 return this.caches.size(); 209 } 210 211 class WatchCaches implements Runnable 212 { 213 private final Map<String, Cacheable<K, V>> caches; 214 215 public WatchCaches(Map<String, Cacheable<K, V>> caches) 216 { 217 this.caches = caches; 218 } 219 220 @Override 221 public void run() 222 { 223 int i = 0; 224 LOG.info("Managing [{}] caches", caches.size()); 225 Set<Map.Entry<String, Cacheable<K, V>>> entries = caches.entrySet(); 226 for (Map.Entry<String, Cacheable<K, V>> entry : entries) 227 { 228 Cacheable<K, V> cacheable = entry.getValue(); 229 Set<Map.Entry<K, Cacheable.Entry<V>>> entrySet = cacheable.entrySet(); 230 for (Map.Entry<K, Cacheable.Entry<V>> cacheableEntry : entrySet) 231 { 232 Cacheable.Entry<V> value = cacheableEntry.getValue(); 233 if (!cacheable.getPolicy().isAlive(value.getTimestamp().getTime())) 234 { 235 cacheable.remove(cacheableEntry.getKey()); 236 LOG.debug("The object [{}] was removed from cache {}", cacheableEntry.getKey(), cacheable.getName()); 237 i++; 238 } 239 } 240 LOG.info("[{}] objects are removed from [{}] cache", i, cacheable.getName()); 241 } 242 } 243 } 244 245}