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}