001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.cache.ram; 020 021import java.io.IOException; 022import java.util.ArrayList; 023import java.util.Iterator; 024import java.util.List; 025import java.util.Map; 026import java.util.Map.Entry; 027import java.util.concurrent.ConcurrentHashMap; 028 029import lucee.commons.io.SystemUtil; 030import lucee.commons.io.cache.CacheEntry; 031import lucee.commons.lang.ExceptionUtil; 032import lucee.runtime.cache.CacheSupport; 033import lucee.runtime.config.Config; 034import lucee.runtime.op.Caster; 035import lucee.runtime.op.Constants; 036import lucee.runtime.type.Struct; 037 038public class RamCache extends CacheSupport { 039 040 public static final int DEFAULT_CONTROL_INTERVAL = 60; 041 private Map<String, RamCacheEntry> entries= new ConcurrentHashMap<String, RamCacheEntry>(); 042 private long missCount; 043 private int hitCount; 044 045 private long idleTime; 046 private long until; 047 private int controlInterval=DEFAULT_CONTROL_INTERVAL*1000; 048 049 050 public RamCache(){ 051 new Controler(this).start(); 052 } 053 054 public static void init(Config config,String[] cacheNames,Struct[] arguments) {//print.ds(); 055 056 } 057 058 @Override 059 public void init(Config config,String cacheName, Struct arguments) throws IOException { 060 // until 061 long until=Caster.toLongValue(arguments.get("timeToLiveSeconds",Constants.LONG_ZERO),Constants.LONG_ZERO)*1000; 062 long idleTime=Caster.toLongValue(arguments.get("timeToIdleSeconds",Constants.LONG_ZERO),Constants.LONG_ZERO)*1000; 063 Object ci = arguments.get("controlIntervall",null); 064 if(ci==null)ci = arguments.get("controlInterval",null); 065 int intervalInSeconds=Caster.toIntValue(ci,DEFAULT_CONTROL_INTERVAL); 066 init(until,idleTime,intervalInSeconds); 067 } 068 069 public RamCache init(long until, long idleTime, int intervalInSeconds) { 070 this.until=until; 071 this.idleTime=idleTime; 072 this.controlInterval=intervalInSeconds*1000; 073 return this; 074 } 075 076 @Override 077 public boolean contains(String key) { 078 return getQuiet(key,null)!=null; 079 } 080 081 082 083 084 public CacheEntry getQuiet(String key, CacheEntry defaultValue) { 085 RamCacheEntry entry = entries.get(key); 086 if(entry==null) { 087 return defaultValue; 088 } 089 if(!valid(entry)) { 090 entries.remove(key); 091 return defaultValue; 092 } 093 return entry; 094 } 095 096 @Override 097 public CacheEntry getCacheEntry(String key, CacheEntry defaultValue) { 098 RamCacheEntry ce = (RamCacheEntry) getQuiet(key, null); 099 if(ce!=null) { 100 hitCount++; 101 return ce.read(); 102 } 103 missCount++; 104 return defaultValue; 105 } 106 107 @Override 108 public long hitCount() { 109 return hitCount; 110 } 111 112 @Override 113 public long missCount() { 114 return missCount; 115 } 116 117 @Override 118 public List<String> keys() { 119 List<String> list=new ArrayList<String>(); 120 121 Iterator<Entry<String, RamCacheEntry>> it = entries.entrySet().iterator(); 122 RamCacheEntry entry; 123 while(it.hasNext()){ 124 entry=it.next().getValue(); 125 if(valid(entry))list.add(entry.getKey()); 126 } 127 return list; 128 } 129 130 public void put(String key, Object value, Long idleTime, Long until) { 131 132 RamCacheEntry entry= entries.get(key); 133 if(entry==null){ 134 entries.put(key, new RamCacheEntry(key,value, 135 idleTime==null?this.idleTime:idleTime.longValue(), 136 until==null?this.until:until.longValue())); 137 } 138 else 139 entry.update(value); 140 } 141 142 public boolean remove(String key) { 143 RamCacheEntry entry = entries.remove(key); 144 if(entry==null) { 145 return false; 146 } 147 return valid(entry); 148 149 } 150 151 public static class Controler extends Thread { 152 153 private RamCache ramCache; 154 155 public Controler(RamCache ramCache) { 156 this.ramCache=ramCache; 157 } 158 159 public void run(){ 160 while(true){ 161 try{ 162 _run(); 163 } 164 catch(Throwable t){ 165 ExceptionUtil.rethrowIfNecessary(t); 166 t.printStackTrace(); 167 } 168 SystemUtil.sleep(ramCache.controlInterval); 169 } 170 } 171 172 private void _run() { 173 RamCacheEntry[] values = ramCache.entries.values().toArray(new RamCacheEntry[ramCache.entries.size()]); 174 for(int i=0;i<values.length;i++){ 175 if(!CacheSupport.valid(values[i])){ 176 ramCache.entries.remove(values[i].getKey()); 177 } 178 } 179 } 180 } 181 182 @Override 183 public int clear() throws IOException { 184 int size=entries.size(); 185 entries.clear(); 186 return size; 187 } 188 189}