org.frameworkset.web.servlet.handler.AbstractUrlHandlerMapping Maven / Gradle / Ivy
Show all versions of bboss-mvc Show documentation
/*
* Copyright 2008 biaoping.yin
*
* 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.frameworkset.web.servlet.handler;
import org.frameworkset.util.AntPathMatcher;
import org.frameworkset.util.Assert;
import org.frameworkset.util.PathMatcher;
import org.frameworkset.util.beans.BeansException;
import org.frameworkset.web.servlet.HandlerExecutionChain;
import org.frameworkset.web.servlet.HandlerMapping;
import org.frameworkset.web.util.UrlPathHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Iterator;
/**
* Title: AbstractUrlHandlerMapping.java
* Description:
* bboss workgroup
* Copyright (c) 2008
* @Date 2010-9-24
* @author biaoping.yin
* @version 1.0
*/
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping{
protected UrlPathHelper urlPathHelper = new UrlPathHelper();
protected PathMatcher pathMatcher = new AntPathMatcher();
protected static Logger logger = LoggerFactory.getLogger(AbstractUrlHandlerMapping.class);
public void destroy()
{
super.destroy();
if(handlerMap != null)
{
handlerMap.clear();handlerMap = null;
}
this.rootHandler = null;
this.urlPathHelper = null;
this.pathMatcher = null;
}
/**
*
*/
private HandlerUrlMappingRegisterTable handlerMap = new HandlerUrlMappingRegisterTable();
private HandlerMeta rootHandler;
private boolean lazyInitHandlers;
/**
* 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.frameworkset.web.util.UrlPathHelper#setAlwaysUseFullPath
*/
public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath);
}
public void setHandlerMap(HandlerUrlMappingRegisterTable handlerMap) {
this.handlerMap = handlerMap;
}
/**
* 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).
* @see org.frameworkset.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.frameworkset.web.servlet.mvc.mutiaction.AbstractUrlMethodNameResolver#setUrlPathHelper
*/
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
this.urlPathHelper = urlPathHelper;
}
/**
* Set the PathMatcher implementation to use for matching URL paths
* against registered URL patterns. Default is AntPathMatcher.
*/
public void setPathMatcher(PathMatcher pathMatcher) {
Assert.notNull(pathMatcher, "PathMatcher must not be null");
this.pathMatcher = pathMatcher;
}
/**
* Return the PathMatcher implementation to use for matching URL paths
* against registered URL patterns.
*/
public PathMatcher getPathMatcher() {
return this.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(HandlerMeta rootHandler) {
this.rootHandler = rootHandler;
}
/**
* Return the root handler for this handler mapping (registered for "/"),
* or null
if none.
*/
public HandlerMeta 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 HandlerExecutionChain getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
HandlerExecutionChain 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.
HandlerMeta rawHandler = null;
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath,lookupPath,request.getRequestURI());
}
}
if (handler != null ) {
if(logger.isDebugEnabled())
logger.debug("Mapping [" + lookupPath + "] to handler '" + handler.getHandler().getHandlerName() + "'");
}
else if (handler == null) {
if(logger.isInfoEnabled())
logger.info("No handler mapping found for [" + lookupPath + "]");
}
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
*/
protected HandlerExecutionChain lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// Direct match?
HandlerMeta handler = this.handlerMap.get(urlPath);
if (handler != null) {
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath,urlPath,request.getRequestURI());
}
// Pattern match?
String bestPathMatch = null;
for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {
String registeredPath = (String) it.next();
if (getPathMatcher().match(registeredPath, urlPath) &&
(bestPathMatch == null || bestPathMatch.length() < registeredPath.length())) {
bestPathMatch = registeredPath;
}
}
if (bestPathMatch != null) {
handler = this.handlerMap.get(bestPathMatch);
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPathMatch, urlPath);
return buildPathExposingHandler(handler, pathWithinMapping,urlPath,request.getRequestURI());
}
// No handler found...
return null;
}
/**
* Validate the given handler against the current request.
*
The default implementation is empty. Can be overridden in subclasses,
* for example to enforce specific preconditions expressed in URL mappings.
* @param handler the handler object to validate
* @param request current HTTP request
* @throws Exception if validation failed
*/
protected void validateHandler(HandlerMeta handler, HttpServletRequest request) throws Exception {
}
/**
* Build a handler object for the given raw handler, exposing the actual
* handler as well as the {@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE}
* before executing the handler.
*
The default implementation builds a {@link HandlerExecutionChain}
* with a special interceptor that exposes the path attribute.
* @param rawHandler the raw handler to expose
* @param pathWithinMapping the path to expose before executing the handler
* @return the final handler object
*/
protected HandlerExecutionChain buildPathExposingHandler(HandlerMeta rawHandler, String pathWithinMapping,String mappingpath,String requesturi) {
// Bean name or resolved handler?
HandlerMeta ret = rawHandler;
if (rawHandler.getHandler() instanceof String) {
String handlerName = (String) rawHandler.getHandler();
ret = new HandlerMeta();
ret.setPathNames(rawHandler.getPathNames());
ret.setHandler(getApplicationContext().getBeanObject(handlerName));
}
HandlerExecutionChain chain = new HandlerExecutionChain(ret);
chain.addInterceptor(new PathExposingHandlerInterceptor(mappingpath,pathWithinMapping,requesturi));
return chain;
}
/**
* 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);
}
/**
* Expose the path the current mapping as request attribute.
* @param mappingPath the path within the current mapping
* @param request the request to expose the path to
* @see #HANDLER_MAPPING_PATH_ATTRIBUTE
*/
protected void exposeHandleMappingPath(String mappingPath, HttpServletRequest request) {
request.setAttribute(HandlerMapping.HANDLER_MAPPING_PATH_ATTRIBUTE, mappingPath);
}
/**
* Expose the path the current mapping as request attribute.
* @param request the request to expose the path to
* @see #HANDLER_MAPPING_PATH_ATTRIBUTE
*/
public static void exposeAttribute(String key,Object value, HttpServletRequest request) {
request.setAttribute(key, value);
}
/**
* Expose the path the current mapping as request attribute.
* @param request the request to expose the path to
* @see #HANDLER_MAPPING_PATH_ATTRIBUTE
*/
public static void removeAttribute(String key,HttpServletRequest request) {
request.removeAttribute(key);
}
/**
* Register the specified handler for the given URL paths.
* @param urlPaths the URLs that the bean should be mapped to
* @throws BeansException if the handler couldn't be registered
* @throws IllegalStateException if there is a conflicting handler registered
*/
protected HandlerMeta registerHandler(String[] urlPaths, HandlerMeta meta) throws Exception, IllegalStateException {
Assert.notNull(urlPaths, "URL path array must not be null");
HandlerMeta ret = null;
for (int j = 0; j < urlPaths.length; j++) {
if(j == 0)
ret = registerHandler(urlPaths[j], meta);
else
registerHandler(urlPaths[j], meta);
}
return ret;
}
/**
* 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 corresponding handler bean)
* @throws BeansException if the handler couldn't be registered
* @throws IllegalStateException if there is a conflicting handler registered
*/
protected HandlerMeta registerHandler(String urlPath, HandlerMeta handler) throws Exception, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
HandlerMeta resolvedHandler = handler;
// Eagerly resolve handler if referencing singleton via name.
if (!this.lazyInitHandlers && handler.getHandler() instanceof String) {
String handlerName = (String) handler.getHandler();
if (getApplicationContext().isSingleton(handlerName)) {
resolvedHandler.setHandler(getApplicationContext().getBeanObject(handlerName));
}
}
HandlerMeta mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
if (mappedHandler.getHandler() != resolvedHandler.getHandler()) {
throw new IllegalStateException(
"Cannot map handler [" + handler.getHandlerName() + "] to URL path [" + urlPath +
"]: There is already handler [" + resolvedHandler.getHandlerName() + "] mapped.");
}
}
else {
if (urlPath.equals("/")) {
if (logger.isDebugEnabled()) {
logger.debug("Root mapping to handler [" + resolvedHandler.getHandlerName() + "]");
}
setRootHandler(resolvedHandler);
}
else if (urlPath.equals("/*")) {
if (logger.isDebugEnabled()) {
logger.debug("Default mapping to handler [" + resolvedHandler.getHandlerName() + "]");
}
setDefaultHandler(resolvedHandler);
}
else {
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isDebugEnabled()) {
logger.debug("Mapped URL path [" + urlPath + "] onto handler [" + resolvedHandler.getHandlerName() + "]");
}
}
}
return resolvedHandler;
}
// /**
// * 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);
// }
/**
* Special interceptor for exposing the
* {@link AbstractUrlHandlerMapping#PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE} attribute.
* @link AbstractUrlHandlerMapping#exposePathWithinMapping
*/
private class PathExposingHandlerInterceptor extends HandlerInterceptorAdapter {
private final String pathWithinMapping;
private final String mappingpath;
private final String requesturi;
public PathExposingHandlerInterceptor(String mappingpath,String pathWithinMapping,String requesturi) {
this.pathWithinMapping = pathWithinMapping;
this.mappingpath = mappingpath;
this.requesturi = requesturi;
}
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, HandlerMeta handler) {
exposePathWithinMapping(this.pathWithinMapping, request);
exposeHandleMappingPath(this.mappingpath, request);
exposeAttribute(HANDLER_REQUESTURI_ATTRIBUTE,this.requesturi, request) ;
return true;
}
}
}