org.springframework.web.servlet.handler.AbstractUrlHandlerMapping Maven / Gradle / Ivy
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.handler;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.BeansException;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.util.PathMatcher;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.util.UrlPathHelper;
/**
* Abstract base class for URL-mapped {@link org.springframework.web.servlet.HandlerMapping}
* implementations. Provides infrastructure for mapping handlers to URLs and configurable
* URL lookup. For information on the latter, see "alwaysUseFullPath" property.
*
* Supports direct matches, e.g. a registered "/test" matches "/test", and
* various Ant-style pattern matches, e.g. a registered "/t*" pattern matches
* both "/test" and "/team", "/test/*" matches all paths in the "/test" directory,
* "/test/**" matches all paths below "/test". For details, see the
* {@link org.springframework.util.AntPathMatcher AntPathMatcher} javadoc.
*
*
Will search all path patterns to find the most exact match for the
* current request path. The most exact match is defined as the longest
* path pattern that matches the current request path.
*
* @author Juergen Hoeller
* @since 16.04.2003
* @see #setAlwaysUseFullPath
* @see #setUrlDecode
* @see org.springframework.util.AntPathMatcher
*/
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
private UrlPathHelper urlPathHelper = new UrlPathHelper();
private PathMatcher pathMatcher = new AntPathMatcher();
private Object rootHandler;
private boolean lazyInitHandlers = false;
private final Map handlerMap = new HashMap();
/**
* Set if URL lookup should always use the full path within the current servlet
* context. Else, the path within the current servlet mapping is used if applicable
* (that is, in the case of a ".../*" servlet mapping in web.xml).
*
Default is "false".
* @see org.springframework.web.util.UrlPathHelper#setAlwaysUseFullPath
*/
public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath);
}
/**
* Set if context path and request URI should be URL-decoded. Both are returned
* undecoded by the Servlet API, in contrast to the servlet path.
*
Uses either the request encoding or the default encoding according
* to the Servlet spec (ISO-8859-1).
*
Note: Setting this to "true" requires JDK 1.4 if the encoding differs
* from the VM's platform default encoding, as JDK 1.3's URLDecoder class
* does not offer a way to specify the encoding.
* @see org.springframework.web.util.UrlPathHelper#setUrlDecode
*/
public void setUrlDecode(boolean urlDecode) {
this.urlPathHelper.setUrlDecode(urlDecode);
}
/**
* Set the UrlPathHelper to use for resolution of lookup paths.
*
Use this to override the default UrlPathHelper with a custom subclass,
* or to share common UrlPathHelper settings across multiple HandlerMappings
* and MethodNameResolvers.
* @see org.springframework.web.servlet.mvc.multiaction.AbstractUrlMethodNameResolver#setUrlPathHelper
*/
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
this.urlPathHelper = urlPathHelper;
}
/**
* Set the PathMatcher implementation to use for matching URL paths
* against registered URL patterns. Default is AntPathMatcher.
* @see org.springframework.util.AntPathMatcher
*/
public void setPathMatcher(PathMatcher pathMatcher) {
Assert.notNull(pathMatcher, "PathMatcher must not be null");
this.pathMatcher = pathMatcher;
}
/**
* Set the root handler for this handler mapping, that is,
* the handler to be registered for the root path ("/").
*
Default is null
, indicating no root handler.
*/
public void setRootHandler(Object rootHandler) {
this.rootHandler = rootHandler;
}
/**
* Return the root handler for this handler mapping (registered for "/"),
* or null
if none.
*/
public Object getRootHandler() {
return this.rootHandler;
}
/**
* Set whether to lazily initialize handlers. Only applicable to
* singleton handlers, as prototypes are always lazily initialized.
* Default is "false", as eager initialization allows for more efficiency
* through referencing the controller objects directly.
*
If you want to allow your controllers to be lazily initialized,
* make them "lazy-init" and set this flag to true. Just making them
* "lazy-init" will not work, as they are initialized through the
* references from the handler mapping in this case.
*/
public void setLazyInitHandlers(boolean lazyInitHandlers) {
this.lazyInitHandlers = lazyInitHandlers;
}
/**
* Look up a handler for the URL path of the given request.
* @param request current HTTP request
* @return the handler instance, or null
if none found
*/
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler for [" + lookupPath + "]");
}
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
if ("/".equals(lookupPath)) {
handler = getRootHandler();
}
if (handler == null) {
handler = getDefaultHandler();
}
if (handler != null) {
exposePathWithinMapping(lookupPath, request);
}
}
return handler;
}
/**
* Look up a handler instance for the given URL path.
*
Supports direct matches, e.g. a registered "/test" matches "/test",
* and various Ant-style pattern matches, e.g. a registered "/t*" matches
* both "/test" and "/team". For details, see the AntPathMatcher class.
*
Looks for the most exact pattern, where most exact is defined as
* the longest path pattern.
* @param urlPath URL the bean is mapped to
* @param request current HTTP request (to expose the path within the mapping to)
* @return the associated handler instance, or null
if not found
* @see #exposePathWithinMapping
* @see org.springframework.util.AntPathMatcher
*/
protected Object lookupHandler(String urlPath, HttpServletRequest request) {
// Direct match?
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
exposePathWithinMapping(urlPath, request);
return handler;
}
// Pattern match?
String bestPathMatch = null;
for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {
String registeredPath = (String) it.next();
if (this.pathMatcher.match(registeredPath, urlPath) &&
(bestPathMatch == null || bestPathMatch.length() <= registeredPath.length())) {
bestPathMatch = registeredPath;
}
}
if (bestPathMatch != null) {
handler = this.handlerMap.get(bestPathMatch);
exposePathWithinMapping(this.pathMatcher.extractPathWithinPattern(bestPathMatch, urlPath), request);
}
return handler;
}
/**
* Expose the path within the current mapping as request attribute.
* @param pathWithinMapping the path within the current mapping
* @param request the request to expose the path to
* @see #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE
*/
protected void exposePathWithinMapping(String pathWithinMapping, HttpServletRequest request) {
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);
}
/**
* Register the specified handler for the given URL paths.
* @param urlPaths the URLs that the bean should be mapped to
* @param beanName the name of the handler bean
* @throws BeansException if the handler couldn't be registered
* @throws IllegalStateException if there is a conflicting handler registered
*/
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
Assert.notNull(urlPaths, "URL path array must not be null");
for (int j = 0; j < urlPaths.length; j++) {
registerHandler(urlPaths[j], beanName);
}
}
/**
* Register the specified handler for the given URL path.
* @param urlPath the URL the bean should be mapped to
* @param handler the handler instance or handler bean name String
* (a bean name will automatically be resolved into the corrresponding handler bean)
* @throws BeansException if the handler couldn't be registered
* @throws IllegalStateException if there is a conflicting handler registered
*/
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
throw new IllegalStateException(
"Cannot map handler [" + handler + "] to URL path [" + urlPath +
"]: There is already handler [" + mappedHandler + "] mapped.");
}
// Eagerly resolve handler if referencing singleton via name.
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
if (getApplicationContext().isSingleton(handlerName)) {
handler = getApplicationContext().getBean(handlerName);
}
}
if (urlPath.equals("/")) {
if (logger.isDebugEnabled()) {
logger.debug("Root mapping to handler [" + handler + "]");
}
setRootHandler(handler);
}
else if (urlPath.equals("/*")) {
if (logger.isDebugEnabled()) {
logger.debug("Default mapping to handler [" + handler + "]");
}
setDefaultHandler(handler);
}
else {
this.handlerMap.put(urlPath, handler);
if (logger.isDebugEnabled()) {
logger.debug("Mapped URL path [" + urlPath + "] onto handler [" + handler + "]");
}
}
}
/**
* Return the registered handlers as an unmodifiable Map, with the registered path
* as key and the handler object (or handler bean name in case of a lazy-init handler)
* as value.
* @see #getDefaultHandler()
*/
public final Map getHandlerMap() {
return Collections.unmodifiableMap(this.handlerMap);
}
}