/** * Copyright (c) 2014, the Railo Company Ltd. * Copyright (c) 2015, Lucee Assosication Switzerland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * */ package lucee.runtime; import static org.apache.commons.collections4.map.AbstractReferenceMap.ReferenceStrength.SOFT; import java.util.Collections; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import lucee.commons.collection.LongKeyList; import lucee.commons.lang.SystemOut; import lucee.runtime.config.ConfigImpl; import lucee.runtime.dump.DumpData; import lucee.runtime.dump.DumpProperties; import lucee.runtime.dump.DumpTable; import lucee.runtime.dump.DumpUtil; import lucee.runtime.dump.Dumpable; import lucee.runtime.dump.SimpleDumpData; import lucee.runtime.type.dt.DateTimeImpl; import lucee.runtime.type.util.ArrayUtil; import org.apache.commons.collections4.map.ReferenceMap; /** * pool to handle pages */ public final class PageSourcePool implements Dumpable { private Map<String,PageSource> pageSources=Collections.synchronizedMap(new ReferenceMap<String,PageSource>(SOFT, SOFT)); //timeout timeout for files private long timeout; //max size of the pool cache private int maxSize; /** * constructor of the class */ public PageSourcePool() { this.timeout=10000; this.maxSize=1000; } /** * return pages matching to key * @param key key for the page * @param updateAccesTime define if do update access time * @return page */ public PageSource getPageSource(String key,boolean updateAccesTime) { PageSource ps=pageSources.get(key.toLowerCase()); if(ps==null) return null; if(updateAccesTime)ps.setLastAccessTime(); return ps; } /** * sts a page object to the page pool * @param key key reference to store page object * @param ps pagesource to store */ public void setPage(String key, PageSource ps) { ps.setLastAccessTime(); pageSources.put(key.toLowerCase(),ps); } /** * returns if page object exists * @param key key reference to a page object * @return has page object or not */ public boolean exists(String key) { return pageSources.containsKey(key.toLowerCase()); } /** * @return returns a array of all keys in the page pool */ public String[] keys() { if(pageSources==null) return new String[0]; Set<String> set = pageSources.keySet(); return set.toArray(new String[set.size()]); } /** * removes a page from the page pool * @param key key reference to page object * @return page object matching to key reference */ public boolean remove(String key) { if(pageSources.remove(key.toLowerCase())!=null) return true; Set<String> set = pageSources.keySet(); String[] keys = set.toArray(new String[set.size()]); // done this way to avoid ConcurrentModificationException PageSource ps; for(String k:keys) { ps=pageSources.get(k); if(key.equalsIgnoreCase(ps.getClassName())) { pageSources.remove(k); return true; } } return false; } public boolean flushPage(String key) { PageSource ps= pageSources.get(key.toLowerCase()); if(ps!=null) { ((PageSourceImpl)ps).flush(); return true; } Iterator<PageSource> it = pageSources.values().iterator(); while(it.hasNext()) { ps = it.next(); if(key.equalsIgnoreCase(ps.getClassName())) { ((PageSourceImpl)ps).flush(); return true; } } return false; } /** * @return returns the size of the pool */ public int size() { return pageSources.size(); } /** * @return returns if pool is empty or not */ public boolean isEmpty() { return pageSources.isEmpty(); } /** * clear unused pages from page pool */ public void clearUnused(ConfigImpl config) { SystemOut.printDate(config.getOutWriter(),"PagePool: "+size()+">("+maxSize+")"); if(size()>maxSize) { String[] keys=keys(); LongKeyList list=new LongKeyList(); for(int i=0;i<keys.length;i++) { PageSource ps= getPageSource(keys[i],false); long updateTime=ps.getLastAccessTime(); if(updateTime+timeout<System.currentTimeMillis()) { long add=((ps.getAccessCount()-1)*10000); if(add>timeout)add=timeout; list.add(updateTime+add,keys[i]); } } while(size()>maxSize) { Object key = list.shift(); if(key==null)break; remove(key.toString()); } } } @Override public DumpData toDumpData(PageContext pageContext,int maxlevel, DumpProperties dp) { maxlevel--; Iterator<PageSource> it = pageSources.values().iterator(); DumpTable table = new DumpTable("#FFCC00","#FFFF00","#000000"); table.setTitle("Page Source Pool"); table.appendRow(1,new SimpleDumpData("Count"),new SimpleDumpData(pageSources.size())); while(it.hasNext()) { PageSource ps= it.next(); DumpTable inner = new DumpTable("#FFCC00","#FFFF00","#000000"); inner.setWidth("100%"); inner.appendRow(1,new SimpleDumpData("source"),new SimpleDumpData(ps.getDisplayPath())); inner.appendRow(1,new SimpleDumpData("last access"),DumpUtil.toDumpData(new DateTimeImpl(pageContext,ps.getLastAccessTime(),false), pageContext,maxlevel,dp)); inner.appendRow(1,new SimpleDumpData("access count"),new SimpleDumpData(ps.getAccessCount())); table.appendRow(1,new SimpleDumpData("Sources"),inner); } return table; } /** * remove all Page from Pool using this classloader * @param cl */ public void clearPages(ClassLoader cl) { synchronized(pageSources){ Iterator<PageSource> it = this.pageSources.values().iterator(); PageSourceImpl entry; while(it.hasNext()) { entry = (PageSourceImpl) it.next(); if(cl!=null)entry.clear(cl); else entry.clear(); } } } public void clear() { pageSources.clear(); } public int getMaxSize() { return maxSize; } }