001package org.javasimon.callback.quantiles;
002
003import org.javasimon.Simon;
004import org.javasimon.Stopwatch;
005
006import java.util.Properties;
007
008/**
009 * Callback which stores data in buckets to compute quantiles.
010 * Buckets are created from configuration stored in a {@link java.util.Properties}
011 * file. Configuration file should look like:
012 * <pre>
013 * # Global default values are set on root
014 * .min=0
015 * .max=60000
016 * .nb=5
017 * .type=LINEAR
018 *
019 * # For org.javasimon group: more buckets
020 * org.javasimon.nb=10
021 *
022 * # SlowClass is a performance bottleneck: higher upper bound
023 * org.javasimon.slow.SlowClass.max=300000
024 *
025 * # Can use Exponential buckets for some Stopwatches
026 * org.javasimon.special.type=EXPONENTIAL
027 * </pre>
028 *
029 * @author gquintana
030 */
031public class PropertiesQuantilesCallback extends QuantilesCallback {
032
033        /** Properties containing configuration. */
034        private final Properties properties;
035
036        /**
037         * Main constructor.
038         *
039         * @param properties Properties containing configuration
040         */
041        public PropertiesQuantilesCallback(Properties properties) {
042                this.properties = properties;
043        }
044
045        /**
046         * Create buckets using callback attributes.
047         *
048         * @param stopwatch Target stopwatch
049         * @return Created buckets for given stopwatch
050         */
051        @Override
052        protected Buckets createBuckets(Stopwatch stopwatch) {
053                // Get configuration
054                BucketsType type = bucketsTypeEnumPropertyType.get(stopwatch, "type");
055                Long min = longPropertyType.get(stopwatch, "min");
056                Long max = longPropertyType.get(stopwatch, "max");
057                Integer nb = integerPropertyType.get(stopwatch, "nb");
058                // Build buckets
059                Buckets buckets = type.createBuckets(stopwatch, min, max, nb);
060                buckets.setLogTemplate(createLogTemplate(stopwatch));
061                return buckets;
062        }
063
064        /**
065         * Returns value of Simon property.
066         *
067         * @param simon Simon
068         * @param name Property name
069         * @return Raw property value
070         */
071        private String getProperty(Simon simon, String name) {
072                return properties.getProperty(simon.getName() + "." + name);
073        }
074
075        /** Remove space at both ends and convert empty strings to null. */
076        private static String cleanString(String s) {
077                if (s != null) {
078                        s = s.trim();
079                        if (s.equals("")) {
080                                s = null;
081                        }
082                }
083                return s;
084        }
085
086        /** Base class for property types. */
087        private abstract class PropertyType<T> {
088                public abstract T parse(String value);
089
090                public T get(Simon simon, String name) {
091                        Simon currentSimon = simon;
092                        T result = null;
093                        while (result == null && currentSimon != null) {
094                                String s = getProperty(currentSimon, name);
095                                s = cleanString(s);
096                                if (s != null) {
097                                        result = parse(s);
098                                }
099                                currentSimon = currentSimon.getParent();
100                        }
101                        return result;
102                }
103        }
104
105        /** Returns long property for Simon. */
106        private final PropertyType<Long> longPropertyType = new PropertyType<Long>() {
107                @Override
108                public Long parse(String s) {
109                        Long l;
110                        try {
111                                l = Long.valueOf(s);
112                        } catch (NumberFormatException numberFormatException) {
113                                l = null;
114                        }
115                        return l;
116                }
117        };
118
119        /** Returns integer property for Simon. */
120        private final PropertyType<Integer> integerPropertyType = new PropertyType<Integer>() {
121                @Override
122                public Integer parse(String s) {
123                        Integer l;
124                        try {
125                                l = Integer.valueOf(s);
126                        } catch (NumberFormatException numberFormatException) {
127                                l = null;
128                        }
129                        return l;
130                }
131        };
132
133        /** Returns enum property for Simon. */
134        private class EnumPropertyType<E extends Enum<E>> extends PropertyType<E> {
135                private final Class<E> enumClass;
136
137                private EnumPropertyType(Class<E> enumClass) {
138                        this.enumClass = enumClass;
139                }
140
141                @Override
142                public E parse(String s) {
143                        E e;
144                        try {
145                                e = Enum.valueOf(enumClass, s.toUpperCase());
146                        } catch (IllegalArgumentException exc) {
147                                e = null;
148                        }
149                        return e;
150                }
151        }
152
153        /** Returns bucket type property for Simon. */
154        private final EnumPropertyType<BucketsType> bucketsTypeEnumPropertyType = new EnumPropertyType<BucketsType>(BucketsType.class) {
155                @Override
156                public BucketsType get(Simon simon, String name) {
157                        BucketsType type = super.get(simon, name);
158                        if (type == null) {
159                                type = BucketsType.LINEAR;
160                        }
161                        return type;
162                }
163        };
164}