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.engine;
020
021import java.util.Collections;
022import java.util.HashMap;
023import java.util.Map;
024
025import lucee.commons.lang.StringUtil;
026import lucee.runtime.PageContext;
027import lucee.runtime.op.Caster;
028
029public abstract class ExecutionLogSupport implements ExecutionLog {
030        
031        protected static final short UNIT_NANO=1;
032        protected static final short UNIT_MICRO=2;
033        protected static final short UNIT_MILLI=4;
034        protected static final short UNIT_UNDEFINED=0;
035        private static final char CHAR_MICRO = (char)181;
036        
037        private Map<String,Pair> map=Collections.synchronizedMap(new HashMap<String,Pair>());
038        protected long min=Long.MIN_VALUE;
039        protected short unit=UNIT_UNDEFINED;
040        
041        public void init(PageContext pc,Map<String,String> arguments) {
042                // min
043                if(min==Long.MIN_VALUE) {
044                        min=toNanos(arguments.get("min-time"),0);
045                }
046                // unit
047                if(UNIT_UNDEFINED==unit){
048                        unit=UNIT_NANO;
049                        // unit
050                        String _unit=arguments.get("unit");
051                        if(_unit!=null) {
052                                _unit=_unit.trim();
053                                if(_unit.equalsIgnoreCase("micro"))             unit=UNIT_MICRO;
054                                else if(_unit.equalsIgnoreCase(CHAR_MICRO+"s")) unit=UNIT_MICRO;
055                                else if(_unit.equalsIgnoreCase("milli"))unit=UNIT_MILLI;
056                                else if(_unit.equalsIgnoreCase("ms"))   unit=UNIT_MILLI;
057                        }
058                }
059                _init(pc,arguments);
060        }
061
062        private static long toNanos(String str, int defaultValue) {
063                if(StringUtil.isEmpty(str)) return defaultValue;
064                str=str.trim().toLowerCase();
065                long l = Caster.toLongValue(str,Long.MIN_VALUE);
066                if(l!=Long.MIN_VALUE) return l;
067                
068                if(str.endsWith("ns")) {
069                        String sub=str.substring(0,str.length()-2);
070                        l = Caster.toLongValue(sub.trim(),Long.MIN_VALUE);
071                        if(l!=Long.MIN_VALUE) return l;
072                }
073                else if(str.endsWith(CHAR_MICRO+"s")) {
074                        String sub=str.substring(0,str.length()-2);
075                        double d = Caster.toDoubleValue(sub.trim(),Double.NaN);
076                        if(!Double.isNaN(d)) return (long)(d*1000);
077                }
078                else if(str.endsWith("ms")) {
079                        String sub=str.substring(0,str.length()-2);
080                        double d = Caster.toDoubleValue(sub.trim(),Double.NaN);
081                        if(!Double.isNaN(d)) return (long)(d*1000*1000);
082                }
083                else if(str.endsWith("s")) {
084                        String sub=str.substring(0,str.length()-1);
085                        double d = Caster.toDoubleValue(sub.trim(),Double.NaN);
086                        if(!Double.isNaN(d)) return (long)(d*1000*1000);
087                }
088                
089                return defaultValue;
090        }
091        
092        public final void release() {
093                map.clear();
094                _release();
095        }
096        
097        public final void start(int pos,String id) {
098                long current = System.nanoTime();
099                map.put(id,new Pair(current,pos));
100        }
101
102        public final void end(int pos,String id) {
103                long current = System.nanoTime();
104                Pair pair=map.remove(id);
105                if(pair!=null) {
106                        if((current-pair.time)>=min)
107                                _log(pair.pos,pos,pair.time,current);
108                }
109        }
110        protected abstract void _init(PageContext pc, Map<String, String> arguments);
111        protected abstract void _log(int startPos, int endPos, long startTime, long endTime);
112        protected abstract void _release();
113
114
115        protected String timeLongToString(long current) {
116                if(unit==UNIT_MICRO) return (current/1000L)+" "+CHAR_MICRO+"s";
117                if(unit==UNIT_MILLI) return (current/1000000L)+" ms";
118                return current+" ns";
119        }
120        protected static String unitShortToString(short unit) {
121                if(unit==UNIT_MICRO) return CHAR_MICRO+"s";
122                if(unit==UNIT_MILLI) return "ms";
123                return "ns";
124        }
125        
126        private final static class Pair {
127                private final long time;
128                private final int pos;
129                public Pair(long time, int pos) {
130                        this.time=time;
131                        this.pos=pos;
132                }
133        }
134}