Package org.springframework.web.servlet.handler

Source Code of org.springframework.web.servlet.handler.AbstractUrlHandlerMapping$PathExposingHandlerInterceptor

/*
* Copyright 2002-2010 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.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.BeansException;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;

/**
* 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.
*
* <p>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.
*
* <p>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
* @author Arjen Poutsma
* @since 16.04.2003
*/
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {

  private Object rootHandler;

  private boolean lazyInitHandlers = false;

  private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();


  /**
   * Set the root handler for this handler mapping, that is,
   * the handler to be registered for the root path ("/").
   * <p>Default is <code>null</code>, indicating no root handler.
   */
  public void setRootHandler(Object rootHandler) {
    this.rootHandler = rootHandler;
  }

  /**
   * Return the root handler for this handler mapping (registered for "/"),
   * or <code>null</code> 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.
   * <p>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 <code>null</code> if none found
   */
  @Override
  protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    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.
      Object rawHandler = null;
      if ("/".equals(lookupPath)) {
        rawHandler = getRootHandler();
      }
      if (rawHandler == null) {
        rawHandler = getDefaultHandler();
      }
      if (rawHandler != null) {
        // Bean name or resolved handler?
        if (rawHandler instanceof String) {
          String handlerName = (String) rawHandler;
          rawHandler = getApplicationContext().getBean(handlerName);
        }
        validateHandler(rawHandler, request);
        handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
      }
    }
    if (handler != null && logger.isDebugEnabled()) {
      logger.debug("Mapping [" + lookupPath + "] to " + handler);
    }
    else if (handler == null && logger.isTraceEnabled()) {
      logger.trace("No handler mapping found for [" + lookupPath + "]");
    }
    return handler;
  }

  /**
   * Look up a handler instance for the given URL path.
   * <p>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.
   * <p>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 <code>null</code> if not found
   * @see #exposePathWithinMapping
   * @see org.springframework.util.AntPathMatcher
   */
  protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
    // Direct match?
    Object handler = this.handlerMap.get(urlPath);
    if (handler != null) {
      // Bean name or resolved handler?
      if (handler instanceof String) {
        String handlerName = (String) handler;
        handler = getApplicationContext().getBean(handlerName);
      }
      validateHandler(handler, request);
      return buildPathExposingHandler(handler, urlPath, urlPath, null);
    }
    // Pattern match?
    List<String> matchingPatterns = new ArrayList<String>();
    for (String registeredPattern : this.handlerMap.keySet()) {
      if (getPathMatcher().match(registeredPattern, urlPath)) {
        matchingPatterns.add(registeredPattern);
      }
    }
    String bestPatternMatch = null;
    Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
    if (!matchingPatterns.isEmpty()) {
      Collections.sort(matchingPatterns, patternComparator);
      if (logger.isDebugEnabled()) {
        logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
      }
      bestPatternMatch = matchingPatterns.get(0);
    }
    if (bestPatternMatch != null) {
      handler = this.handlerMap.get(bestPatternMatch);
      // Bean name or resolved handler?
      if (handler instanceof String) {
        String handlerName = (String) handler;
        handler = getApplicationContext().getBean(handlerName);
      }
      validateHandler(handler, request);
      String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);

      // There might be multiple 'best patterns', let's make sure we have the correct URI template variables
      // for all of them
      Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
      for (String matchingPattern : matchingPatterns) {
        if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {
          uriTemplateVariables
              .putAll(getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath));
        }
      }
      if (logger.isDebugEnabled()) {
        logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
      }
      return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
    }
    // No handler found...
    return null;
  }

  /**
   * Validate the given handler against the current request.
   * <p>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(Object handler, HttpServletRequest request) throws Exception {
  }
 
  /**
   * Build a handler object for the given raw handler, exposing the actual
   * handler, the {@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE}, as well as
   * the {@link #URI_TEMPLATE_VARIABLES_ATTRIBUTE} before executing the handler.
   * <p>The default implementation builds a {@link HandlerExecutionChain}
   * with a special interceptor that exposes the path attribute and uri template variables
   * @param rawHandler the raw handler to expose
   * @param pathWithinMapping the path to expose before executing the handler
   * @param uriTemplateVariables the URI template variables, can be <code>null</code> if no variables found
   * @return the final handler object
   */
  protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
      String pathWithinMapping, Map<String, String> uriTemplateVariables) {

    HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
    chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
    if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
      chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
    }
    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 bestMatchingPattern, String pathWithinMapping, HttpServletRequest request) {
    request.setAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, bestMatchingPattern);
    request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);
  }

  /**
   * Expose the URI templates variables as request attribute.
   * @param uriTemplateVariables the URI template variables
   * @param request the request to expose the path to
   * @see #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE
   */
  protected void exposeUriTemplateVariables(Map<String, String> uriTemplateVariables, HttpServletRequest request) {
    request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
  }

  /**
   * 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 (String urlPath : urlPaths) {
      registerHandler(urlPath, 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 corresponding 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 resolvedHandler = handler;

    // Eagerly resolve handler if referencing singleton via name.
    if (!this.lazyInitHandlers && handler instanceof String) {
      String handlerName = (String) handler;
      if (getApplicationContext().isSingleton(handlerName)) {
        resolvedHandler = getApplicationContext().getBean(handlerName);
      }
    }

    Object mappedHandler = this.handlerMap.get(urlPath);
    if (mappedHandler != null) {
      if (mappedHandler != resolvedHandler) {
        throw new IllegalStateException(
            "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
            "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
      }
    }
    else {
      if (urlPath.equals("/")) {
        if (logger.isInfoEnabled()) {
          logger.info("Root mapping to " + getHandlerDescription(handler));
        }
        setRootHandler(resolvedHandler);
      }
      else if (urlPath.equals("/*")) {
        if (logger.isInfoEnabled()) {
          logger.info("Default mapping to " + getHandlerDescription(handler));
        }
        setDefaultHandler(resolvedHandler);
      }
      else {
        this.handlerMap.put(urlPath, resolvedHandler);
        if (logger.isInfoEnabled()) {
          logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
        }
      }
    }
  }

  private String getHandlerDescription(Object handler) {
    return "handler " + (handler instanceof String ? "'" + handler + "'" : "of type [" + handler.getClass() + "]");
  }


  /**
   * 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<String, Object> getHandlerMap() {
    return Collections.unmodifiableMap(this.handlerMap);
  }

  /**
   * Indicates whether this handler mapping support type-level mappings. Default to {@code false}.
   */
  protected boolean supportsTypeLevelMappings() {
    return false;
  }


  /**
   * Special interceptor for exposing the
   * {@link AbstractUrlHandlerMapping#PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE} attribute.
   * @see AbstractUrlHandlerMapping#exposePathWithinMapping
   */
  private class PathExposingHandlerInterceptor extends HandlerInterceptorAdapter {

    private final String bestMatchingPattern;

    private final String pathWithinMapping;

    public PathExposingHandlerInterceptor(String bestMatchingPattern, String pathWithinMapping) {
      this.bestMatchingPattern = bestMatchingPattern;
      this.pathWithinMapping = pathWithinMapping;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
      exposePathWithinMapping(this.bestMatchingPattern, this.pathWithinMapping, request);
      request.setAttribute(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING, supportsTypeLevelMappings());
      return true;
    }

  }

  /**
   * Special interceptor for exposing the
   * {@link AbstractUrlHandlerMapping#URI_TEMPLATE_VARIABLES_ATTRIBUTE} attribute.
   * @see AbstractUrlHandlerMapping#exposePathWithinMapping
   */
  private class UriTemplateVariablesHandlerInterceptor extends HandlerInterceptorAdapter {

    private final Map<String, String> uriTemplateVariables;

    public UriTemplateVariablesHandlerInterceptor(Map<String, String> uriTemplateVariables) {
      this.uriTemplateVariables = uriTemplateVariables;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
      exposeUriTemplateVariables(this.uriTemplateVariables, request);
      return true;
    }
  }

}
TOP

Related Classes of org.springframework.web.servlet.handler.AbstractUrlHandlerMapping$PathExposingHandlerInterceptor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
-20639858-1', 'auto'); ga('send', 'pageview');