001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.servlet.handler;
018
019import javax.servlet.http.HttpServletRequest;
020import javax.servlet.http.HttpServletResponse;
021
022import org.springframework.util.ObjectUtils;
023import org.springframework.util.PathMatcher;
024import org.springframework.web.context.request.WebRequestInterceptor;
025import org.springframework.web.servlet.HandlerInterceptor;
026import org.springframework.web.servlet.ModelAndView;
027
028/**
029 * Contains and delegates calls to a {@link HandlerInterceptor} along with
030 * include (and optionally exclude) path patterns to which the interceptor should apply.
031 * Also provides matching logic to test if the interceptor applies to a given request path.
032 *
033 * <p>A MappedInterceptor can be registered directly with any
034 * {@link org.springframework.web.servlet.handler.AbstractHandlerMethodMapping}.
035 * Furthermore, beans of type {@code MappedInterceptor} are automatically detected by
036 * {@code AbstractHandlerMethodMapping} (including ancestor ApplicationContext's) which
037 * effectively means the interceptor is registered "globally" with all handler mappings.
038 *
039 * @author Keith Donald
040 * @author Rossen Stoyanchev
041 * @author Brian Clozel
042 * @since 3.0
043 */
044public final class MappedInterceptor implements HandlerInterceptor {
045
046        private final String[] includePatterns;
047
048        private final String[] excludePatterns;
049
050        private final HandlerInterceptor interceptor;
051
052        private PathMatcher pathMatcher;
053
054
055        /**
056         * Create a new MappedInterceptor instance.
057         * @param includePatterns the path patterns to map (empty for matching to all paths)
058         * @param interceptor the HandlerInterceptor instance to map to the given patterns
059         */
060        public MappedInterceptor(String[] includePatterns, HandlerInterceptor interceptor) {
061                this(includePatterns, null, interceptor);
062        }
063
064        /**
065         * Create a new MappedInterceptor instance.
066         * @param includePatterns the path patterns to map (empty for matching to all paths)
067         * @param excludePatterns the path patterns to exclude (empty for no specific excludes)
068         * @param interceptor the HandlerInterceptor instance to map to the given patterns
069         */
070        public MappedInterceptor(String[] includePatterns, String[] excludePatterns, HandlerInterceptor interceptor) {
071                this.includePatterns = includePatterns;
072                this.excludePatterns = excludePatterns;
073                this.interceptor = interceptor;
074        }
075
076
077        /**
078         * Create a new MappedInterceptor instance.
079         * @param includePatterns the path patterns to map (empty for matching to all paths)
080         * @param interceptor the WebRequestInterceptor instance to map to the given patterns
081         */
082        public MappedInterceptor(String[] includePatterns, WebRequestInterceptor interceptor) {
083                this(includePatterns, null, interceptor);
084        }
085
086        /**
087         * Create a new MappedInterceptor instance.
088         * @param includePatterns the path patterns to map (empty for matching to all paths)
089         * @param excludePatterns the path patterns to exclude (empty for no specific excludes)
090         * @param interceptor the WebRequestInterceptor instance to map to the given patterns
091         */
092        public MappedInterceptor(String[] includePatterns, String[] excludePatterns, WebRequestInterceptor interceptor) {
093                this(includePatterns, excludePatterns, new WebRequestHandlerInterceptorAdapter(interceptor));
094        }
095
096
097        /**
098         * Configure a PathMatcher to use with this MappedInterceptor instead of the one passed
099         * by default to the {@link #matches(String, org.springframework.util.PathMatcher)} method.
100         * <p>This is an advanced property that is only required when using custom PathMatcher
101         * implementations that support mapping metadata other than the Ant-style path patterns
102         * supported by default.
103         */
104        public void setPathMatcher(PathMatcher pathMatcher) {
105                this.pathMatcher = pathMatcher;
106        }
107
108        /**
109         * The configured PathMatcher, or {@code null} if none.
110         */
111        public PathMatcher getPathMatcher() {
112                return this.pathMatcher;
113        }
114
115        /**
116         * The path into the application the interceptor is mapped to.
117         */
118        public String[] getPathPatterns() {
119                return this.includePatterns;
120        }
121
122        /**
123         * The actual {@link HandlerInterceptor} reference.
124         */
125        public HandlerInterceptor getInterceptor() {
126                return this.interceptor;
127        }
128
129
130        /**
131         * Determine a match for the given lookup path.
132         * @param lookupPath the current request path
133         * @param pathMatcher a path matcher for path pattern matching
134         */
135        public boolean matches(String lookupPath, PathMatcher pathMatcher) {
136                PathMatcher pathMatcherToUse = (this.pathMatcher != null ? this.pathMatcher : pathMatcher);
137                if (!ObjectUtils.isEmpty(this.excludePatterns)) {
138                        for (String pattern : this.excludePatterns) {
139                                if (pathMatcherToUse.match(pattern, lookupPath)) {
140                                        return false;
141                                }
142                        }
143                }
144                if (ObjectUtils.isEmpty(this.includePatterns)) {
145                        return true;
146                }
147                for (String pattern : this.includePatterns) {
148                        if (pathMatcherToUse.match(pattern, lookupPath)) {
149                                return true;
150                        }
151                }
152                return false;
153        }
154
155        @Override
156        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
157                        throws Exception {
158
159                return this.interceptor.preHandle(request, response, handler);
160        }
161
162        @Override
163        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
164                        ModelAndView modelAndView) throws Exception {
165
166                this.interceptor.postHandle(request, response, handler, modelAndView);
167        }
168
169        @Override
170        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
171                        Exception ex) throws Exception {
172
173                this.interceptor.afterCompletion(request, response, handler, ex);
174        }
175
176}