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;
020
021import java.util.Collections;
022import java.util.Iterator;
023import java.util.Map;
024import java.util.Map.Entry;
025
026import lucee.commons.collection.LongKeyList;
027import lucee.commons.lang.SizeOf;
028import lucee.commons.lang.SystemOut;
029import lucee.runtime.config.ConfigImpl;
030import lucee.runtime.dump.DumpData;
031import lucee.runtime.dump.DumpProperties;
032import lucee.runtime.dump.DumpTable;
033import lucee.runtime.dump.DumpUtil;
034import lucee.runtime.dump.Dumpable;
035import lucee.runtime.dump.SimpleDumpData;
036import lucee.runtime.type.Sizeable;
037import lucee.runtime.type.dt.DateTimeImpl;
038import lucee.runtime.type.util.ArrayUtil;
039
040import org.apache.commons.collections.map.ReferenceMap;
041
042/**
043 * pool to handle pages
044 */
045public final class PageSourcePool implements Dumpable,Sizeable {
046        
047        private Map<Object,PageSource> pageSources=Collections.synchronizedMap(new ReferenceMap(ReferenceMap.SOFT, ReferenceMap.SOFT));
048        //timeout timeout for files
049        private long timeout;
050        //max size of the pool cache
051        private int maxSize;
052                
053        /**
054         * constructor of the class
055         */
056        public PageSourcePool() {
057                this.timeout=10000;
058                this.maxSize=1000;
059        }
060        
061        /**
062         * return pages matching to key
063         * @param key key for the page
064         * @param updateAccesTime define if do update access time
065         * @return page
066         */
067        public PageSource getPageSource(Object key,boolean updateAccesTime) {// this method is used from Morpheus
068                Object o=pageSources.get(key);
069                if(o==null) return null;
070                
071                PageSource ps=(PageSource) o;
072                if(updateAccesTime)ps.setLastAccessTime();
073                return ps;
074        }
075
076        
077        /**
078         * sts a page object to the page pool
079         * @param key key reference to store page object
080         * @param ps pagesource to store
081         */
082        public void setPage(Object key, PageSource ps) {
083                ps.setLastAccessTime();
084                pageSources.put(key,ps);
085        }
086        
087        /**
088         * returns if page object exists
089         * @param key key reference to a page object
090         * @return has page object or not
091         */
092        public boolean exists(Object key) {
093                return pageSources.containsKey(key);
094        }
095        
096        /**
097         * @return returns a array of all keys in the page pool
098         */
099        public Object[] keys() {
100                return ArrayUtil.keys(pageSources);
101        }
102        
103        /**
104         * removes a page from the page pool
105         * @param key key reference to page object
106         * @return page object matching to key reference
107         */
108        public boolean remove(Object key) {
109                return pageSources.remove(key)!=null;
110        }
111        
112        /**
113         * @return returns the size of the pool
114         */
115        public int size() {
116                return pageSources.size();
117        }
118        
119        /**
120         * @return returns if pool is empty or not
121         */
122        public boolean isEmpty() {
123                return pageSources.isEmpty();
124        }
125        
126        /**
127         * clear unused pages from page pool
128         */
129        public void clearUnused(ConfigImpl config) {
130                
131                SystemOut.printDate(config.getOutWriter(),"PagePool: "+size()+">("+maxSize+")");
132                if(size()>maxSize) {
133                        Object[] keys=keys();
134                        LongKeyList list=new LongKeyList();
135                        for(int i=0;i<keys.length;i++) {
136                            PageSource ps= getPageSource(keys[i],false);
137                                long updateTime=ps.getLastAccessTime();
138                                if(updateTime+timeout<System.currentTimeMillis()) {
139                                        long add=((ps.getAccessCount()-1)*10000);
140                                        if(add>timeout)add=timeout;
141                                        list.add(updateTime+add,keys[i]);
142                                }
143                        }
144                        while(size()>maxSize) {
145                                Object key = list.shift();
146                                if(key==null)break;
147                                remove(key);
148                        }
149                }
150        }
151
152        @Override
153        public DumpData toDumpData(PageContext pageContext,int maxlevel, DumpProperties dp) {
154                maxlevel--;
155                Iterator<Object> it = pageSources.keySet().iterator();
156                
157                
158                DumpTable table = new DumpTable("#FFCC00","#FFFF00","#000000");
159                table.setTitle("Page Source Pool");
160                table.appendRow(1,new SimpleDumpData("Count"),new SimpleDumpData(pageSources.size()));
161                while(it.hasNext()) {
162                    PageSource ps= pageSources.get(it.next());
163                    DumpTable inner = new DumpTable("#FFCC00","#FFFF00","#000000");
164                        inner.setWidth("100%");
165                        inner.appendRow(1,new SimpleDumpData("source"),new SimpleDumpData(ps.getDisplayPath()));
166                        inner.appendRow(1,new SimpleDumpData("last access"),DumpUtil.toDumpData(new DateTimeImpl(pageContext,ps.getLastAccessTime(),false), pageContext,maxlevel,dp));
167                        inner.appendRow(1,new SimpleDumpData("access count"),new SimpleDumpData(ps.getAccessCount()));
168                        table.appendRow(1,new SimpleDumpData("Sources"),inner);
169                }
170                return table;
171        }
172        
173        /**
174         * remove all Page from Pool using this classloader
175         * @param cl 
176         */
177        public void clearPages(ClassLoader cl) {
178                synchronized(pageSources){
179                        Iterator<Entry<Object, PageSource>> it = this.pageSources.entrySet().iterator();
180                        PageSourceImpl entry;
181                        while(it.hasNext()) {
182                                entry = (PageSourceImpl) it.next().getValue();
183                                if(cl!=null)entry.clear(cl);
184                                else entry.clear();
185                        }
186                }
187        }
188
189        public void clear() {
190                pageSources.clear();
191        }
192        public int getMaxSize() {
193                return maxSize;
194        }
195
196        @Override
197        public long sizeOf() {
198                return SizeOf.size(this.timeout)
199                +SizeOf.size(this.maxSize)
200                +SizeOf.size(this.pageSources);
201        }
202}