| 1 16 17 package org.springframework.web.servlet; 18 19 import java.io.IOException ; 20 import java.util.ArrayList ; 21 import java.util.Collections ; 22 import java.util.Enumeration ; 23 import java.util.HashMap ; 24 import java.util.HashSet ; 25 import java.util.Iterator ; 26 import java.util.List ; 27 import java.util.Locale ; 28 import java.util.Map ; 29 import java.util.Properties ; 30 import java.util.Set ; 31 32 import javax.servlet.ServletException ; 33 import javax.servlet.http.HttpServletRequest ; 34 import javax.servlet.http.HttpServletResponse ; 35 36 import org.apache.commons.logging.Log; 37 import org.apache.commons.logging.LogFactory; 38 39 import org.springframework.beans.BeansException; 40 import org.springframework.beans.factory.BeanFactoryUtils; 41 import org.springframework.beans.factory.BeanInitializationException; 42 import org.springframework.beans.factory.NoSuchBeanDefinitionException; 43 import org.springframework.beans.factory.config.AutowireCapableBeanFactory; 44 import org.springframework.context.i18n.LocaleContext; 45 import org.springframework.context.i18n.LocaleContextHolder; 46 import org.springframework.core.OrderComparator; 47 import org.springframework.core.io.ClassPathResource; 48 import org.springframework.core.io.support.PropertiesLoaderUtils; 49 import org.springframework.ui.context.ThemeSource; 50 import org.springframework.util.ClassUtils; 51 import org.springframework.util.StringUtils; 52 import org.springframework.web.context.request.RequestAttributes; 53 import org.springframework.web.context.request.RequestContextHolder; 54 import org.springframework.web.context.request.ServletRequestAttributes; 55 import org.springframework.web.multipart.MultipartException; 56 import org.springframework.web.multipart.MultipartHttpServletRequest; 57 import org.springframework.web.multipart.MultipartResolver; 58 import org.springframework.web.util.NestedServletException; 59 import org.springframework.web.util.UrlPathHelper; 60 import org.springframework.web.util.WebUtils; 61 62 143 public class DispatcherServlet extends FrameworkServlet { 144 145 148 public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver"; 149 150 153 public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver"; 154 155 158 public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver"; 159 160 165 public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping"; 166 167 172 public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter"; 173 174 179 public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver"; 180 181 185 public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator"; 186 187 192 public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver"; 193 194 198 public static final String HANDLER_EXECUTION_CHAIN_ATTRIBUTE = DispatcherServlet.class.getName() + ".HANDLER"; 199 200 205 public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT"; 206 207 211 public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER"; 212 213 217 public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER"; 218 219 223 public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE"; 224 225 226 229 public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound"; 230 231 235 private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties"; 236 237 238 241 protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY); 242 243 private static final Properties defaultStrategies; 244 245 static { 246 try { 250 ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class); 251 defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); 252 } 253 catch (IOException ex) { 254 throw new IllegalStateException ("Could not load 'DispatcherServlet.properties': " + ex.getMessage()); 255 } 256 } 257 258 259 260 private boolean detectAllHandlerMappings = true; 261 262 263 private boolean detectAllHandlerAdapters = true; 264 265 266 private boolean detectAllHandlerExceptionResolvers = true; 267 268 269 private boolean detectAllViewResolvers = true; 270 271 272 private boolean cleanupAfterInclude = true; 273 274 275 private boolean threadContextInheritable = false; 276 277 278 279 private MultipartResolver multipartResolver; 280 281 282 private LocaleResolver localeResolver; 283 284 285 private ThemeResolver themeResolver; 286 287 288 private List handlerMappings; 289 290 291 private List handlerAdapters; 292 293 294 private List handlerExceptionResolvers; 295 296 297 private RequestToViewNameTranslator viewNameTranslator; 298 299 300 private List viewResolvers; 301 302 303 310 public void setDetectAllHandlerMappings(boolean detectAllHandlerMappings) { 311 this.detectAllHandlerMappings = detectAllHandlerMappings; 312 } 313 314 321 public void setDetectAllHandlerAdapters(boolean detectAllHandlerAdapters) { 322 this.detectAllHandlerAdapters = detectAllHandlerAdapters; 323 } 324 325 332 public void setDetectAllHandlerExceptionResolvers(boolean detectAllHandlerExceptionResolvers) { 333 this.detectAllHandlerExceptionResolvers = detectAllHandlerExceptionResolvers; 334 } 335 336 343 public void setDetectAllViewResolvers(boolean detectAllViewResolvers) { 344 this.detectAllViewResolvers = detectAllViewResolvers; 345 } 346 347 360 public void setCleanupAfterInclude(boolean cleanupAfterInclude) { 361 this.cleanupAfterInclude = cleanupAfterInclude; 362 } 363 364 376 public void setThreadContextInheritable(boolean threadContextInheritable) { 377 this.threadContextInheritable = threadContextInheritable; 378 } 379 380 381 387 protected void initFrameworkServlet() throws ServletException , BeansException { 388 initMultipartResolver(); 389 initLocaleResolver(); 390 initThemeResolver(); 391 initHandlerMappings(); 392 initHandlerAdapters(); 393 initHandlerExceptionResolvers(); 394 initRequestToViewNameTranslator(); 395 initViewResolvers(); 396 } 397 398 403 private void initMultipartResolver() { 404 try { 405 this.multipartResolver = (MultipartResolver) 406 getWebApplicationContext().getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class); 407 if (logger.isDebugEnabled()) { 408 logger.debug("Using MultipartResolver [" + this.multipartResolver + "]"); 409 } 410 } 411 catch (NoSuchBeanDefinitionException ex) { 412 this.multipartResolver = null; 414 if (logger.isDebugEnabled()) { 415 logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME + 416 "': no multipart request handling provided"); 417 } 418 } 419 } 420 421 426 private void initLocaleResolver() { 427 try { 428 this.localeResolver = (LocaleResolver) 429 getWebApplicationContext().getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class); 430 if (logger.isDebugEnabled()) { 431 logger.debug("Using LocaleResolver [" + this.localeResolver + "]"); 432 } 433 } 434 catch (NoSuchBeanDefinitionException ex) { 435 this.localeResolver = (LocaleResolver) getDefaultStrategy(LocaleResolver.class); 437 if (logger.isDebugEnabled()) { 438 logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME + 439 "': using default [" + this.localeResolver + "]"); 440 } 441 } 442 } 443 444 449 private void initThemeResolver() { 450 try { 451 this.themeResolver = (ThemeResolver) 452 getWebApplicationContext().getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class); 453 if (logger.isDebugEnabled()) { 454 logger.debug("Using ThemeResolver [" + this.themeResolver + "]"); 455 } 456 } 457 catch (NoSuchBeanDefinitionException ex) { 458 this.themeResolver = (ThemeResolver) getDefaultStrategy(ThemeResolver.class); 460 if (logger.isDebugEnabled()) { 461 logger.debug("Unable to locate ThemeResolver with name '" + THEME_RESOLVER_BEAN_NAME + 462 "': using default [" + this.themeResolver + "]"); 463 } 464 } 465 } 466 467 472 private void initHandlerMappings() { 473 if (this.detectAllHandlerMappings) { 474 Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( 477 getWebApplicationContext(), HandlerMapping.class, true, false); 478 if (!matchingBeans.isEmpty()) { 479 this.handlerMappings = new ArrayList (matchingBeans.values()); 480 Collections.sort(this.handlerMappings, new OrderComparator()); 482 } 483 } 484 else { 485 try { 486 Object hm = getWebApplicationContext().getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); 487 this.handlerMappings = Collections.singletonList(hm); 488 } 489 catch (NoSuchBeanDefinitionException ex) { 490 } 492 } 493 494 if (this.handlerMappings == null) { 497 this.handlerMappings = getDefaultStrategies(HandlerMapping.class); 498 if (logger.isDebugEnabled()) { 499 logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default"); 500 } 501 } 502 } 503 504 509 private void initHandlerAdapters() { 510 if (this.detectAllHandlerAdapters) { 511 Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( 514 getWebApplicationContext(), HandlerAdapter.class, true, false); 515 if (!matchingBeans.isEmpty()) { 516 this.handlerAdapters = new ArrayList (matchingBeans.values()); 517 Collections.sort(this.handlerAdapters, new OrderComparator()); 519 } 520 } 521 else { 522 try { 523 Object ha = getWebApplicationContext().getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class); 524 this.handlerAdapters = Collections.singletonList(ha); 525 } 526 catch (NoSuchBeanDefinitionException ex) { 527 } 529 } 530 531 if (this.handlerAdapters == null) { 534 this.handlerAdapters = getDefaultStrategies(HandlerAdapter.class); 535 if (logger.isDebugEnabled()) { 536 logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default"); 537 } 538 } 539 } 540 541 546 private void initHandlerExceptionResolvers() { 547 if (this.detectAllHandlerExceptionResolvers) { 548 Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( 551 getWebApplicationContext(), HandlerExceptionResolver.class, true, false); 552 this.handlerExceptionResolvers = new ArrayList (matchingBeans.values()); 553 Collections.sort(this.handlerExceptionResolvers, new OrderComparator()); 555 } 556 else { 557 try { 558 Object her = getWebApplicationContext().getBean( 559 HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class); 560 this.handlerExceptionResolvers = Collections.singletonList(her); 561 } 562 catch (NoSuchBeanDefinitionException ex) { 563 this.handlerExceptionResolvers = getDefaultStrategies(HandlerExceptionResolver.class); 565 } 566 } 567 } 568 569 573 private void initRequestToViewNameTranslator() { 574 try { 575 this.viewNameTranslator = (RequestToViewNameTranslator) getWebApplicationContext().getBean( 576 REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class); 577 if (logger.isDebugEnabled()) { 578 logger.debug("Using RequestToViewNameTranslator [" + this.viewNameTranslator + "]"); 579 } 580 } 581 catch (NoSuchBeanDefinitionException ex) { 582 this.viewNameTranslator = 584 (RequestToViewNameTranslator) getDefaultStrategy(RequestToViewNameTranslator.class); 585 if (logger.isDebugEnabled()) { 586 logger.debug("Unable to locate RequestToViewNameTranslator with name '" + 587 REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME + 588 "': using default [" + this.viewNameTranslator + "]"); 589 } 590 } 591 } 592 593 598 private void initViewResolvers() { 599 if (this.detectAllViewResolvers) { 600 Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( 603 getWebApplicationContext(), ViewResolver.class, true, false); 604 if (!matchingBeans.isEmpty()) { 605 this.viewResolvers = new ArrayList (matchingBeans.values()); 606 Collections.sort(this.viewResolvers, new OrderComparator()); 608 } 609 } 610 else { 611 try { 612 Object vr = getWebApplicationContext().getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class); 613 this.viewResolvers = Collections.singletonList(vr); 614 } 615 catch (NoSuchBeanDefinitionException ex) { 616 } 618 } 619 620 if (this.viewResolvers == null) { 623 this.viewResolvers = getDefaultStrategies(ViewResolver.class); 624 if (logger.isDebugEnabled()) { 625 logger.debug("No ViewResolvers found in servlet '" + getServletName() + "': using default"); 626 } 627 } 628 } 629 630 636 public ThemeSource getThemeSource() { 637 if (getWebApplicationContext() instanceof ThemeSource) { 638 return (ThemeSource) getWebApplicationContext(); 639 } 640 else { 641 return null; 642 } 643 } 644 645 646 655 protected Object getDefaultStrategy(Class strategyInterface) throws BeansException { 656 List strategies = getDefaultStrategies(strategyInterface); 657 if (strategies.size() != 1) { 658 throw new BeanInitializationException( 659 "DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]"); 660 } 661 return strategies.get(0); 662 } 663 664 673 protected List getDefaultStrategies(Class strategyInterface) throws BeansException { 674 String key = strategyInterface.getName(); 675 List strategies = null; 676 String value = defaultStrategies.getProperty(key); 677 if (value != null) { 678 String [] classNames = StringUtils.commaDelimitedListToStringArray(value); 679 strategies = new ArrayList (classNames.length); 680 for (int i = 0; i < classNames.length; i++) { 681 String className = classNames[i]; 682 try { 683 Class clazz = ClassUtils.forName(className, getClass().getClassLoader()); 684 Object strategy = createDefaultStrategy(clazz); 685 strategies.add(strategy); 686 } 687 catch (ClassNotFoundException ex) { 688 throw new BeanInitializationException( 689 "Could not find DispatcherServlet's default strategy class [" + className + 690 "] for interface [" + key + "]", ex); 691 } 692 catch (LinkageError err) { 693 throw new BeanInitializationException( 694 "Error loading DispatcherServlet's default strategy class [" + className + 695 "] for interface [" + key + "]: problem with class file or dependent class", err); 696 } 697 } 698 } 699 else { 700 strategies = Collections.EMPTY_LIST; 701 } 702 return strategies; 703 } 704 705 716 protected Object createDefaultStrategy(Class clazz) throws BeansException { 717 return getWebApplicationContext().getAutowireCapableBeanFactory().createBean( 718 clazz, AutowireCapableBeanFactory.AUTOWIRE_NO, false); 719 } 720 721 722 726 protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { 727 if (logger.isDebugEnabled()) { 728 logger.debug("DispatcherServlet with name '" + getServletName() + "' received request for [" + 729 request.getRequestURI() + "]"); 730 } 731 732 Map attributesSnapshot = null; 735 if (WebUtils.isIncludeRequest(request)) { 736 logger.debug("Taking snapshot of request attributes before include"); 737 attributesSnapshot = new HashMap (); 738 Enumeration attrNames = request.getAttributeNames(); 739 while (attrNames.hasMoreElements()) { 740 String attrName = (String ) attrNames.nextElement(); 741 if (this.cleanupAfterInclude || attrName.startsWith(DispatcherServlet.class.getName())) { 742 attributesSnapshot.put(attrName, request.getAttribute(attrName)); 743 } 744 } 745 } 746 747 request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); 749 request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); 750 request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); 751 request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); 752 753 try { 754 doDispatch(request, response); 755 } 756 finally { 757 if (attributesSnapshot != null) { 759 restoreAttributesAfterInclude(request, attributesSnapshot); 760 } 761 } 762 } 763 764 775 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { 776 HttpServletRequest processedRequest = request; 777 HandlerExecutionChain mappedHandler = null; 778 int interceptorIndex = -1; 779 780 |