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