View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.web.servlet.handler;
18  
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.List;
22  import javax.servlet.http.HttpServletRequest;
23  
24  import org.springframework.beans.BeansException;
25  import org.springframework.beans.factory.BeanFactoryUtils;
26  import org.springframework.core.Ordered;
27  import org.springframework.util.AntPathMatcher;
28  import org.springframework.util.Assert;
29  import org.springframework.util.PathMatcher;
30  import org.springframework.web.context.request.WebRequestInterceptor;
31  import org.springframework.web.context.support.WebApplicationObjectSupport;
32  import org.springframework.web.servlet.HandlerExecutionChain;
33  import org.springframework.web.servlet.HandlerInterceptor;
34  import org.springframework.web.servlet.HandlerMapping;
35  import org.springframework.web.util.UrlPathHelper;
36  
37  /**
38   * Abstract base class for {@link org.springframework.web.servlet.HandlerMapping}
39   * implementations. Supports ordering, a default handler, handler interceptors,
40   * including handler interceptors mapped by path patterns.
41   *
42   * <p>Note: This base class does <i>not</i> support exposure of the
43   * {@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE}. Support for this attribute
44   * is up to concrete subclasses, typically based on request URL mappings.
45   *
46   * @author Juergen Hoeller
47   * @author Rossen Stoyanchev
48   * @since 07.04.2003
49   * @see #getHandlerInternal
50   * @see #setDefaultHandler
51   * @see #setAlwaysUseFullPath
52   * @see #setUrlDecode
53   * @see org.springframework.util.AntPathMatcher
54   * @see #setInterceptors
55   * @see org.springframework.web.servlet.HandlerInterceptor
56   */
57  public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
58  		implements HandlerMapping, Ordered {
59  
60  	private int order = Integer.MAX_VALUE;  // default: same as non-Ordered
61  
62  	private Object defaultHandler;
63  
64  	private UrlPathHelper urlPathHelper = new UrlPathHelper();
65  
66  	private PathMatcher pathMatcher = new AntPathMatcher();
67  
68  	private final List<Object> interceptors = new ArrayList<Object>();
69  
70  	private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>();
71  
72  	private final List<MappedInterceptor> mappedInterceptors = new ArrayList<MappedInterceptor>();
73  
74  
75  	/**
76  	 * Specify the order value for this HandlerMapping bean.
77  	 * <p>Default value is {@code Integer.MAX_VALUE}, meaning that it's non-ordered.
78  	 * @see org.springframework.core.Ordered#getOrder()
79  	 */
80  	public final void setOrder(int order) {
81  	  this.order = order;
82  	}
83  
84  	@Override
85  	public final int getOrder() {
86  	  return this.order;
87  	}
88  
89  	/**
90  	 * Set the default handler for this handler mapping.
91  	 * This handler will be returned if no specific mapping was found.
92  	 * <p>Default is {@code null}, indicating no default handler.
93  	 */
94  	public void setDefaultHandler(Object defaultHandler) {
95  		this.defaultHandler = defaultHandler;
96  	}
97  
98  	/**
99  	 * Return the default handler for this handler mapping,
100 	 * or {@code null} if none.
101 	 */
102 	public Object getDefaultHandler() {
103 		return this.defaultHandler;
104 	}
105 
106 	/**
107 	 * Set if URL lookup should always use the full path within the current servlet
108 	 * context. Else, the path within the current servlet mapping is used if applicable
109 	 * (that is, in the case of a ".../*" servlet mapping in web.xml).
110 	 * <p>Default is "false".
111 	 * @see org.springframework.web.util.UrlPathHelper#setAlwaysUseFullPath
112 	 */
113 	public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
114 		this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath);
115 	}
116 
117 	/**
118 	 * Set if context path and request URI should be URL-decoded. Both are returned
119 	 * <i>undecoded</i> by the Servlet API, in contrast to the servlet path.
120 	 * <p>Uses either the request encoding or the default encoding according
121 	 * to the Servlet spec (ISO-8859-1).
122 	 * @see org.springframework.web.util.UrlPathHelper#setUrlDecode
123 	 */
124 	public void setUrlDecode(boolean urlDecode) {
125 		this.urlPathHelper.setUrlDecode(urlDecode);
126 	}
127 
128 	/**
129 	 * Set if ";" (semicolon) content should be stripped from the request URI.
130 	 * <p>The default value is {@code true}.
131 	 * @see org.springframework.web.util.UrlPathHelper#setRemoveSemicolonContent(boolean)
132 	 */
133 	public void setRemoveSemicolonContent(boolean removeSemicolonContent) {
134 		this.urlPathHelper.setRemoveSemicolonContent(removeSemicolonContent);
135 	}
136 
137 	/**
138 	 * Set the UrlPathHelper to use for resolution of lookup paths.
139 	 * <p>Use this to override the default UrlPathHelper with a custom subclass,
140 	 * or to share common UrlPathHelper settings across multiple HandlerMappings
141 	 * and MethodNameResolvers.
142 	 */
143 	public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
144 		Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
145 		this.urlPathHelper = urlPathHelper;
146 	}
147 
148 	/**
149 	 * Return the UrlPathHelper implementation to use for resolution of lookup paths.
150 	 */
151 	public UrlPathHelper getUrlPathHelper() {
152 		return urlPathHelper;
153 	}
154 
155 	/**
156 	 * Set the PathMatcher implementation to use for matching URL paths
157 	 * against registered URL patterns. Default is AntPathMatcher.
158 	 * @see org.springframework.util.AntPathMatcher
159 	 */
160 	public void setPathMatcher(PathMatcher pathMatcher) {
161 		Assert.notNull(pathMatcher, "PathMatcher must not be null");
162 		this.pathMatcher = pathMatcher;
163 	}
164 
165 	/**
166 	 * Return the PathMatcher implementation to use for matching URL paths
167 	 * against registered URL patterns.
168 	 */
169 	public PathMatcher getPathMatcher() {
170 		return this.pathMatcher;
171 	}
172 
173 	/**
174 	 * Set the interceptors to apply for all handlers mapped by this handler mapping.
175 	 * <p>Supported interceptor types are HandlerInterceptor, WebRequestInterceptor, and MappedInterceptor.
176 	 * Mapped interceptors apply only to request URLs that match its path patterns.
177 	 * Mapped interceptor beans are also detected by type during initialization.
178 	 * @param interceptors array of handler interceptors, or {@code null} if none
179 	 * @see #adaptInterceptor
180 	 * @see org.springframework.web.servlet.HandlerInterceptor
181 	 * @see org.springframework.web.context.request.WebRequestInterceptor
182 	 */
183 	public void setInterceptors(Object[] interceptors) {
184 		this.interceptors.addAll(Arrays.asList(interceptors));
185 	}
186 
187 
188 	/**
189 	 * Initializes the interceptors.
190 	 * @see #extendInterceptors(java.util.List)
191 	 * @see #initInterceptors()
192 	 */
193 	@Override
194 	protected void initApplicationContext() throws BeansException {
195 		extendInterceptors(this.interceptors);
196 		detectMappedInterceptors(this.mappedInterceptors);
197 		initInterceptors();
198 	}
199 
200 	/**
201 	 * Extension hook that subclasses can override to register additional interceptors,
202 	 * given the configured interceptors (see {@link #setInterceptors}).
203 	 * <p>Will be invoked before {@link #initInterceptors()} adapts the specified
204 	 * interceptors into {@link HandlerInterceptor} instances.
205 	 * <p>The default implementation is empty.
206 	 * @param interceptors the configured interceptor List (never {@code null}), allowing
207 	 * to add further interceptors before as well as after the existing interceptors
208 	 */
209 	protected void extendInterceptors(List<Object> interceptors) {
210 	}
211 
212 	/**
213 	 * Detect beans of type {@link MappedInterceptor} and add them to the list of mapped interceptors.
214 	 * <p>This is called in addition to any {@link MappedInterceptor}s that may have been provided
215 	 * via {@link #setInterceptors}, by default adding all beans of type {@link MappedInterceptor}
216 	 * from the current context and its ancestors. Subclasses can override and refine this policy.
217 	 * @param mappedInterceptors an empty list to add {@link MappedInterceptor} instances to
218 	 */
219 	protected void detectMappedInterceptors(List<MappedInterceptor> mappedInterceptors) {
220 		mappedInterceptors.addAll(
221 				BeanFactoryUtils.beansOfTypeIncludingAncestors(
222 						getApplicationContext(), MappedInterceptor.class, true, false).values());
223 	}
224 
225 	/**
226 	 * Initialize the specified interceptors, checking for {@link MappedInterceptor}s and
227 	 * adapting {@link HandlerInterceptor}s and {@link WebRequestInterceptor}s if necessary.
228 	 * @see #setInterceptors
229 	 * @see #adaptInterceptor
230 	 */
231 	protected void initInterceptors() {
232 		if (!this.interceptors.isEmpty()) {
233 			for (int i = 0; i < this.interceptors.size(); i++) {
234 				Object interceptor = this.interceptors.get(i);
235 				if (interceptor == null) {
236 					throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
237 				}
238 				if (interceptor instanceof MappedInterceptor) {
239 					this.mappedInterceptors.add((MappedInterceptor) interceptor);
240 				}
241 				else {
242 					this.adaptedInterceptors.add(adaptInterceptor(interceptor));
243 				}
244 			}
245 		}
246 	}
247 
248 	/**
249 	 * Adapt the given interceptor object to the {@link HandlerInterceptor} interface.
250 	 * <p>By default, the supported interceptor types are {@link HandlerInterceptor}
251 	 * and {@link WebRequestInterceptor}. Each given {@link WebRequestInterceptor}
252 	 * will be wrapped in a {@link WebRequestHandlerInterceptorAdapter}.
253 	 * Can be overridden in subclasses.
254 	 * @param interceptor the specified interceptor object
255 	 * @return the interceptor wrapped as HandlerInterceptor
256 	 * @see org.springframework.web.servlet.HandlerInterceptor
257 	 * @see org.springframework.web.context.request.WebRequestInterceptor
258 	 * @see WebRequestHandlerInterceptorAdapter
259 	 */
260 	protected HandlerInterceptor adaptInterceptor(Object interceptor) {
261 		if (interceptor instanceof HandlerInterceptor) {
262 			return (HandlerInterceptor) interceptor;
263 		}
264 		else if (interceptor instanceof WebRequestInterceptor) {
265 			return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
266 		}
267 		else {
268 			throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
269 		}
270 	}
271 
272 	/**
273 	 * Return the adapted interceptors as {@link HandlerInterceptor} array.
274 	 * @return the array of {@link HandlerInterceptor}s, or {@code null} if none
275 	 */
276 	protected final HandlerInterceptor[] getAdaptedInterceptors() {
277 		int count = this.adaptedInterceptors.size();
278 		return (count > 0 ? this.adaptedInterceptors.toArray(new HandlerInterceptor[count]) : null);
279 	}
280 
281 	/**
282 	 * Return all configured {@link MappedInterceptor}s as an array.
283 	 * @return the array of {@link MappedInterceptor}s, or {@code null} if none
284 	 */
285 	protected final MappedInterceptor[] getMappedInterceptors() {
286 		int count = this.mappedInterceptors.size();
287 		return (count > 0 ? this.mappedInterceptors.toArray(new MappedInterceptor[count]) : null);
288 	}
289 
290 	/**
291 	 * Look up a handler for the given request, falling back to the default
292 	 * handler if no specific one is found.
293 	 * @param request current HTTP request
294 	 * @return the corresponding handler instance, or the default handler
295 	 * @see #getHandlerInternal
296 	 */
297 	@Override
298 	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
299 		Object handler = getHandlerInternal(request);
300 		if (handler == null) {
301 			handler = getDefaultHandler();
302 		}
303 		if (handler == null) {
304 			return null;
305 		}
306 		// Bean name or resolved handler?
307 		if (handler instanceof String) {
308 			String handlerName = (String) handler;
309 			handler = getApplicationContext().getBean(handlerName);
310 		}
311 		return getHandlerExecutionChain(handler, request);
312 	}
313 
314 	/**
315 	 * Look up a handler for the given request, returning {@code null} if no
316 	 * specific one is found. This method is called by {@link #getHandler};
317 	 * a {@code null} return value will lead to the default handler, if one is set.
318 	 * <p>Note: This method may also return a pre-built {@link HandlerExecutionChain},
319 	 * combining a handler object with dynamically determined interceptors.
320 	 * Statically specified interceptors will get merged into such an existing chain.
321 	 * @param request current HTTP request
322 	 * @return the corresponding handler instance, or {@code null} if none found
323 	 * @throws Exception if there is an internal error
324 	 */
325 	protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
326 
327 	/**
328 	 * Build a {@link HandlerExecutionChain} for the given handler, including
329 	 * applicable interceptors.
330 	 * <p>The default implementation builds a standard {@link HandlerExecutionChain}
331 	 * with the given handler, the handler mapping's common interceptors, and any
332 	 * {@link MappedInterceptor}s matching to the current request URL. Subclasses
333 	 * may override this in order to extend/rearrange the list of interceptors.
334 	 * <p><b>NOTE:</b> The passed-in handler object may be a raw handler or a
335 	 * pre-built {@link HandlerExecutionChain}. This method should handle those
336 	 * two cases explicitly, either building a new {@link HandlerExecutionChain}
337 	 * or extending the existing chain.
338 	 * <p>For simply adding an interceptor in a custom subclass, consider calling
339 	 * {@code super.getHandlerExecutionChain(handler, request)} and invoking
340 	 * {@link HandlerExecutionChain#addInterceptor} on the returned chain object.
341 	 * @param handler the resolved handler instance (never {@code null})
342 	 * @param request current HTTP request
343 	 * @return the HandlerExecutionChain (never {@code null})
344 	 * @see #getAdaptedInterceptors()
345 	 */
346 	protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
347 		HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
348 				(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
349 		chain.addInterceptors(getAdaptedInterceptors());
350 
351 		String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
352 		for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) {
353 			if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
354 				chain.addInterceptor(mappedInterceptor.getInterceptor());
355 			}
356 		}
357 
358 		return chain;
359 	}
360 
361 }