001package org.javasimon.javaee; 002 003import java.lang.reflect.InvocationTargetException; 004import javax.servlet.FilterConfig; 005import javax.servlet.http.HttpServletRequest; 006 007import org.javasimon.Manager; 008import org.javasimon.Stopwatch; 009import org.javasimon.javaee.reqreporter.DefaultRequestReporter; 010import org.javasimon.javaee.reqreporter.RequestReporter; 011import org.javasimon.source.MonitorSource; 012import org.javasimon.source.StopwatchSource; 013import org.javasimon.utils.Replacer; 014import org.javasimon.utils.SimonUtils; 015 016/** 017 * Various supporting utility methods for {@link SimonServletFilter}. 018 * 019 * @author virgo47@gmail.com 020 */ 021public 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 | IllegalAccessException | InstantiationException | ClassCastException e) { 108 throw new IllegalArgumentException("Invalid Stopwatch source class name", e); 109 } 110 } 111 112 @SuppressWarnings("unchecked") 113 private static StopwatchSource<HttpServletRequest> monitorSourceNewInstance(Manager manager, Class<?> monitorClass) throws InstantiationException, IllegalAccessException { 114 StopwatchSource<HttpServletRequest> stopwatchSource = null; 115 try { 116 stopwatchSource = (StopwatchSource<HttpServletRequest>) monitorClass.getConstructor(Manager.class).newInstance(manager); 117 } catch (NoSuchMethodException | InvocationTargetException e) { 118 // safe to ignore here - we'll try default constructor + setter 119 } 120 if (stopwatchSource == null) { 121 stopwatchSource = (StopwatchSource<HttpServletRequest>) monitorClass.newInstance(); 122 try { 123 monitorClass.getMethod("setManager", Manager.class).invoke(stopwatchSource, manager); 124 } catch (NoSuchMethodException | InvocationTargetException e) { 125 throw new IllegalArgumentException("Stopwatch source class must have public constructor or public setter with Manager argument (used class " + monitorClass.getName() + ")", e); 126 } 127 } 128 return stopwatchSource; 129 } 130 131 /** 132 * Returns RequestReporter for the class specified for context parameter {@link SimonServletFilter#INIT_PARAM_REQUEST_REPORTER_CLASS}. 133 */ 134 public static RequestReporter initRequestReporter(FilterConfig filterConfig) { 135 String className = filterConfig.getInitParameter(SimonServletFilter.INIT_PARAM_REQUEST_REPORTER_CLASS); 136 137 if (className == null) { 138 return new DefaultRequestReporter(); 139 } else { 140 try { 141 return (RequestReporter) Class.forName(className).newInstance(); 142 } catch (ClassNotFoundException | IllegalAccessException | InstantiationException classNotFoundException) { 143 throw new IllegalArgumentException("Invalid Request reporter class name", classNotFoundException); 144 } 145 } 146 } 147}