001package org.javasimon.spring.webmvc;
002
003import javax.servlet.http.HttpServletRequest;
004import javax.servlet.http.HttpServletResponse;
005
006import org.javasimon.Manager;
007import org.javasimon.SimonManager;
008import org.javasimon.Split;
009import org.javasimon.source.StopwatchSource;
010
011import org.springframework.web.servlet.HandlerInterceptor;
012import org.springframework.web.servlet.ModelAndView;
013
014/**
015 * Spring MVC interceptor monitors time spent in handlers (usually controllers)
016 * and views. Spring configuration:
017 * <pre>{@code
018 * <mvc:interceptors>
019 *    <bean class="org.javasimon.spring.webmvc.MonitoringHandlerInterceptor"/>
020 * </mvc:interceptors>
021 * }</pre>
022 *
023 * @author gquintana
024 * @since Spring 3.1
025 */
026public class MonitoringHandlerInterceptor implements HandlerInterceptor {
027        /**
028         * Current thread running split, if any.
029         */
030        private final ThreadLocal<HandlerLocation> threadLocation = new ThreadLocal<>();
031
032        /**
033         * Stopwatch source.
034         */
035        private StopwatchSource<HandlerLocation> stopwatchSource;
036
037        /**
038         * Constructor with stopwatch source.
039         *
040         * @param stopwatchSource Stopwatch source
041         */
042        public MonitoringHandlerInterceptor(StopwatchSource<HandlerLocation> stopwatchSource) {
043                this.stopwatchSource = stopwatchSource;
044        }
045
046        /**
047         * Constructor with simon manager and default stopwatch source.
048         *
049         * @param manager Manager manager
050         */
051        public MonitoringHandlerInterceptor(Manager manager) {
052                stopwatchSource = new HandlerStopwatchSource(manager);
053        }
054
055        /**
056         * Default constructor: default stopwatch source, default manager.
057         */
058        public MonitoringHandlerInterceptor() {
059                stopwatchSource = new HandlerStopwatchSource(SimonManager.manager());
060        }
061
062        /**
063         * Start stopwatch for given name and thread.
064         *
065         * @return Running split
066         */
067        protected final Split startStopwatch(HandlerLocation location) {
068                Split split = stopwatchSource.start(location);
069                location.setSplit(split);
070                return split;
071        }
072
073        /**
074         * Stop current thread stopwatch (if any).
075         *
076         * @return Stopped split
077         */
078        protected final Split stopStopwatch() {
079                HandlerLocation location = threadLocation.get();
080                Split split = null;
081                if (location != null) {
082                        split = location.getSplit();
083                        split.stop();
084                        location.setSplit(null);
085                }
086                return split;
087        }
088
089        /**
090         * Invoked before controller.
091         */
092        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
093                final HandlerLocation location = new HandlerLocation(request, handler, HandlerStep.CONTROLLER);
094                threadLocation.set(location);
095
096                // Start controller stopwatch
097                startStopwatch(location);
098                return true;
099        }
100
101        /**
102         * Invoked between controller and view.
103         */
104        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
105                // Stop controller stopwatch
106                stopStopwatch();
107
108                HandlerLocation location = threadLocation.get();
109                location.setStep(HandlerStep.VIEW);
110
111                // Start view stopwatch
112                startStopwatch(location);
113        }
114
115        /**
116         * Invoked after view.
117         */
118        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
119                // Stop view stopwatch
120                stopStopwatch();
121
122                // Remove location
123                threadLocation.remove();
124        }
125}