001    package org.javasimon.javaee;
002    
003    import java.lang.reflect.InvocationTargetException;
004    import javax.servlet.FilterConfig;
005    import javax.servlet.http.HttpServletRequest;
006    
007    import org.javasimon.Manager;
008    import org.javasimon.Stopwatch;
009    import org.javasimon.javaee.reqreporter.DefaultRequestReporter;
010    import org.javasimon.javaee.reqreporter.RequestReporter;
011    import org.javasimon.source.MonitorSource;
012    import org.javasimon.source.StopwatchSource;
013    import org.javasimon.utils.Replacer;
014    import org.javasimon.utils.SimonUtils;
015    
016    /**
017     * Various supporting utility methods for {@link SimonServletFilter}.
018     *
019     * @author virgo47@gmail.com
020     */
021    public class SimonServletFilterUtils {
022            /**
023             * Regex replacer for any number of slashes or dots for a single dot.
024             */
025            private static final Replacer TO_DOT_PATTERN = new Replacer("[/.]+", ".");
026    
027            /**
028             * Creates new replacer for unallowed characters in the URL. This inverts character group for name pattern
029             * ({@link SimonUtils#NAME_PATTERN_CHAR_CLASS_CONTENT}) and replaces its dot with slash too (dots are to be
030             * replaced, slashs preserved in this step of URL processing).
031             *
032             * @param replacement replacement string (for every unallowed character)
033             * @return compiled pattern matching characters to remove from the URL
034             */
035            static Replacer createUnallowedCharsReplacer(String replacement) {
036                    return new Replacer("[^" + SimonUtils.NAME_PATTERN_CHAR_CLASS_CONTENT.replace('.', '/') + "]+", replacement);
037            }
038    
039            /**
040             * Returns Simon name for the specified request (local name without any configured prefix). By default dots and all non-simon-name
041             * compliant characters are removed first, then all slashes are switched to dots (repeating slashes make one dot).
042             *
043             * @param uri request URI
044             * @param unallowedCharacterReplacer replacer for characters that are not allowed in Simon name
045             * @return local part of the Simon name for the request URI (without prefix)
046             */
047            public static String getSimonName(String uri, Replacer unallowedCharacterReplacer) {
048                    if (uri.startsWith("/")) {
049                            uri = uri.substring(1);
050                    }
051                    String name = unallowedCharacterReplacer.process(uri);
052                    name = TO_DOT_PATTERN.process(name);
053                    return name;
054            }
055    
056            /**
057             * Create and initialize the stopwatch source depending on the filter init parameters. Both
058             * monitor source class ({@link SimonServletFilter#INIT_PARAM_STOPWATCH_SOURCE_CLASS} and whether
059             * to cache results ({@link SimonServletFilter#INIT_PARAM_STOPWATCH_SOURCE_CACHE}) can be adjusted.
060             *
061             * @param filterConfig Filter configuration
062             * @return Stopwatch source
063             */
064            protected static StopwatchSource<HttpServletRequest> initStopwatchSource(FilterConfig filterConfig, Manager manager) {
065                    String stopwatchSourceClass = filterConfig.getInitParameter(SimonServletFilter.INIT_PARAM_STOPWATCH_SOURCE_CLASS);
066                    StopwatchSource<HttpServletRequest> stopwatchSource = createMonitorSource(stopwatchSourceClass, manager);
067    
068                    injectSimonPrefixIntoMonitorSource(filterConfig, stopwatchSource);
069    
070                    String cache = filterConfig.getInitParameter(SimonServletFilter.INIT_PARAM_STOPWATCH_SOURCE_CACHE);
071                    stopwatchSource = wrapMonitorSourceWithCacheIfNeeded(stopwatchSource, cache);
072    
073                    return stopwatchSource;
074            }
075    
076            private static StopwatchSource<HttpServletRequest> createMonitorSource(String stopwatchSourceClass, Manager manager) {
077                    if (stopwatchSourceClass == null) {
078                            return new HttpStopwatchSource(manager);
079                    } else {
080                            return createMonitorForSourceSpecifiedClass(stopwatchSourceClass, manager);
081                    }
082            }
083    
084            private static void injectSimonPrefixIntoMonitorSource(FilterConfig filterConfig, MonitorSource<HttpServletRequest, Stopwatch> stopwatchSource) {
085                    String simonPrefix = filterConfig.getInitParameter(SimonServletFilter.INIT_PARAM_PREFIX);
086                    if (simonPrefix != null) {
087                            if (stopwatchSource instanceof HttpStopwatchSource) {
088                                    HttpStopwatchSource httpStopwatchSource = (HttpStopwatchSource) stopwatchSource;
089                                    httpStopwatchSource.setPrefix(simonPrefix);
090                            } else {
091                                    throw new IllegalArgumentException("Prefix init param is only compatible with HttpStopwatchSource");
092                            }
093                    }
094            }
095    
096            private static StopwatchSource<HttpServletRequest> wrapMonitorSourceWithCacheIfNeeded(StopwatchSource<HttpServletRequest> stopwatchSource, String cache) {
097                    if (cache != null && Boolean.parseBoolean(cache)) {
098                            stopwatchSource = HttpStopwatchSource.newCacheStopwatchSource(stopwatchSource);
099                    }
100                    return stopwatchSource;
101            }
102    
103            private static StopwatchSource<HttpServletRequest> createMonitorForSourceSpecifiedClass(String stopwatchSourceClass, Manager manager) {
104                    try {
105                            Class<?> monitorClass = Class.forName(stopwatchSourceClass);
106                            return  monitorSourceNewInstance(manager, monitorClass);
107                    } catch (ClassNotFoundException e) {
108                            throw new IllegalArgumentException("Invalid Stopwatch source class name", e);
109                    } catch (ClassCastException e) {
110                            throw new IllegalArgumentException("Invalid Stopwatch source class name", e);
111                    } catch (InstantiationException e) {
112                            throw new IllegalArgumentException("Invalid Stopwatch source class name", e);
113                    } catch (IllegalAccessException e) {
114                            throw new IllegalArgumentException("Invalid Stopwatch source class name", e);
115                    }
116            }
117    
118            @SuppressWarnings("unchecked")
119            private static StopwatchSource<HttpServletRequest> monitorSourceNewInstance(Manager manager, Class<?> monitorClass) throws InstantiationException, IllegalAccessException {
120                    StopwatchSource<HttpServletRequest> stopwatchSource = null;
121                    try {
122                            stopwatchSource = (StopwatchSource<HttpServletRequest>) monitorClass.getConstructor(Manager.class).newInstance(manager);
123                    } catch (NoSuchMethodException e) {
124                            // safe to ignore here - we'll try default constructor + setter
125                    } catch (InvocationTargetException e) {
126                            // safe to ignore here
127                    }
128                    if (stopwatchSource == null) {
129                            stopwatchSource = (StopwatchSource<HttpServletRequest>) monitorClass.newInstance();
130                            try {
131                                    monitorClass.getMethod("setManager", Manager.class).invoke(stopwatchSource, manager);
132                            } catch (NoSuchMethodException e) {
133                                    throw new IllegalArgumentException("Stopwatch source class must have public constructor or public setter with Manager argument (used class " + monitorClass.getName() + ")", e);
134                            } catch (InvocationTargetException e) {
135                                    throw new IllegalArgumentException("Stopwatch source class must have public constructor or public setter with Manager argument (used class " + monitorClass.getName() + ")", e);
136                            }
137                    }
138                    return stopwatchSource;
139            }
140    
141            /**
142             * Returns RequestReporter for the class specified for context parameter {@link SimonServletFilter#INIT_PARAM_REQUEST_REPORTER_CLASS}.
143             */
144            public static RequestReporter initRequestReporter(FilterConfig filterConfig) {
145                    String className = filterConfig.getInitParameter(SimonServletFilter.INIT_PARAM_REQUEST_REPORTER_CLASS);
146    
147                    if (className == null) {
148                            return new DefaultRequestReporter();
149                    } else {
150                            try {
151                                    return (RequestReporter) Class.forName(className).newInstance();
152                            } catch (ClassNotFoundException classNotFoundException) {
153                                    throw new IllegalArgumentException("Invalid Request reporter class name", classNotFoundException);
154                            } catch (InstantiationException instantiationException) {
155                                    throw new IllegalArgumentException("Invalid Request reporter class name", instantiationException);
156                            } catch (IllegalAccessException illegalAccessException) {
157                                    throw new IllegalArgumentException("Invalid Request reporter class name", illegalAccessException);
158                            }
159                    }
160            }
161    }