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.lang.Nullable;
023import org.springframework.util.ObjectUtils;
024import org.springframework.util.PathMatcher;
025import org.springframework.web.context.request.WebRequestInterceptor;
026import org.springframework.web.servlet.HandlerInterceptor;
027import org.springframework.web.servlet.ModelAndView;
028
029/**
030 * Contains and delegates calls to a {@link HandlerInterceptor} along with
031 * include (and optionally exclude) path patterns to which the interceptor should apply.
032 * Also provides matching logic to test if the interceptor applies to a given request path.
033 *
034 * <p>A MappedInterceptor can be registered directly with any
035 * {@link org.springframework.web.servlet.handler.AbstractHandlerMethodMapping}.
036 * Furthermore, beans of type {@code MappedInterceptor} are automatically detected by
037 * {@code AbstractHandlerMethodMapping} (including ancestor ApplicationContext's) which
038 * effectively means the interceptor is registered "globally" with all handler mappings.
039 *
040 * @author Keith Donald
041 * @author Rossen Stoyanchev
042 * @author Brian Clozel
043 * @since 3.0
044 */
045public final class MappedInterceptor implements HandlerInterceptor {
046
047        @Nullable
048        private final String[] includePatterns;
049
050        @Nullable
051        private final String[] excludePatterns;
052
053        private final HandlerInterceptor interceptor;
054
055        @Nullable
056        private PathMatcher pathMatcher;
057
058
059        /**
060         * Create a new MappedInterceptor instance.
061         * @param includePatterns the path patterns to map (empty for matching to all paths)
062         * @param interceptor the HandlerInterceptor instance to map to the given patterns
063         */
064        public MappedInterceptor(@Nullable String[] includePatterns, HandlerInterceptor interceptor) {
065                this(includePatterns, null, interceptor);
066        }
067
068        /**
069         * Create a new MappedInterceptor instance.
070         * @param includePatterns the path patterns to map (empty for matching to all paths)
071         * @param excludePatterns the path patterns to exclude (empty for no specific excludes)
072         * @param interceptor the HandlerInterceptor instance to map to the given patterns
073         */
074        public MappedInterceptor(@Nullable String[] includePatterns, @Nullable String[] excludePatterns,
075                        HandlerInterceptor interceptor) {
076
077                this.includePatterns = includePatterns;
078                this.excludePatterns = excludePatterns;
079                this.interceptor = interceptor;
080        }
081
082
083        /**
084         * Create a new MappedInterceptor instance.
085         * @param includePatterns the path patterns to map (empty for matching to all paths)
086         * @param interceptor the WebRequestInterceptor instance to map to the given patterns
087         */
088        public MappedInterceptor(@Nullable String[] includePatterns, WebRequestInterceptor interceptor) {
089                this(includePatterns, null, interceptor);
090        }
091
092        /**
093         * Create a new MappedInterceptor instance.
094         * @param includePatterns the path patterns to map (empty for matching to all paths)
095         * @param excludePatterns the path patterns to exclude (empty for no specific excludes)
096         * @param interceptor the WebRequestInterceptor instance to map to the given patterns
097         */
098        public MappedInterceptor(@Nullable String[] includePatterns, @Nullable String[] excludePatterns,
099                        WebRequestInterceptor interceptor) {
100
101                this(includePatterns, excludePatterns, new WebRequestHandlerInterceptorAdapter(interceptor));
102        }
103
104
105        /**
106         * Configure a PathMatcher to use with this MappedInterceptor instead of the one passed
107         * by default to the {@link #matches(String, org.springframework.util.PathMatcher)} method.
108         * <p>This is an advanced property that is only required when using custom PathMatcher
109         * implementations that support mapping metadata other than the Ant-style path patterns
110         * supported by default.
111         */
112        public void setPathMatcher(@Nullable PathMatcher pathMatcher) {
113                this.pathMatcher = pathMatcher;
114        }
115
116        /**
117         * The configured PathMatcher, or {@code null} if none.
118         */
119        @Nullable
120        public PathMatcher getPathMatcher() {
121                return this.pathMatcher;
122        }
123
124        /**
125         * The path into the application the interceptor is mapped to.
126         */
127        @Nullable
128        public String[] getPathPatterns() {
129                return this.includePatterns;
130        }
131
132        /**
133         * The actual {@link HandlerInterceptor} reference.
134         */
135        public HandlerInterceptor getInterceptor() {
136                return this.interceptor;
137        }
138
139
140        /**
141         * Determine a match for the given lookup path.
142         * @param lookupPath the current request path
143         * @param pathMatcher a path matcher for path pattern matching
144         * @return {@code true} if the interceptor applies to the given request path
145         */
146        public boolean matches(String lookupPath, PathMatcher pathMatcher) {
147                PathMatcher pathMatcherToUse = (this.pathMatcher != null ? this.pathMatcher : pathMatcher);
148                if (!ObjectUtils.isEmpty(this.excludePatterns)) {
149                        for (String pattern : this.excludePatterns) {
150                                if (pathMatcherToUse.match(pattern, lookupPath)) {
151                                        return false;
152                                }
153                        }
154                }
155                if (ObjectUtils.isEmpty(this.includePatterns)) {
156                        return true;
157                }
158                for (String pattern : this.includePatterns) {
159                        if (pathMatcherToUse.match(pattern, lookupPath)) {
160                                return true;
161                        }
162                }
163                return false;
164        }
165
166        @Override
167        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
168                        throws Exception {
169
170                return this.interceptor.preHandle(request, response, handler);
171        }
172
173        @Override
174        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
175                        @Nullable ModelAndView modelAndView) throws Exception {
176
177                this.interceptor.postHandle(request, response, handler, modelAndView);
178        }
179
180        @Override
181        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
182                        @Nullable Exception ex) throws Exception {
183
184                this.interceptor.afterCompletion(request, response, handler, ex);
185        }
186
187}