001/* 002 * Copyright 2002-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.portlet; 018 019import java.io.IOException; 020import java.util.ArrayList; 021import java.util.Collections; 022import java.util.Enumeration; 023import java.util.Iterator; 024import java.util.LinkedList; 025import java.util.List; 026import java.util.Map; 027import java.util.Properties; 028import javax.portlet.ActionRequest; 029import javax.portlet.ActionResponse; 030import javax.portlet.EventRequest; 031import javax.portlet.EventResponse; 032import javax.portlet.MimeResponse; 033import javax.portlet.PortletException; 034import javax.portlet.PortletRequest; 035import javax.portlet.PortletRequestDispatcher; 036import javax.portlet.PortletResponse; 037import javax.portlet.PortletSession; 038import javax.portlet.RenderRequest; 039import javax.portlet.RenderResponse; 040import javax.portlet.ResourceRequest; 041import javax.portlet.ResourceResponse; 042import javax.portlet.StateAwareResponse; 043 044import org.apache.commons.logging.Log; 045import org.apache.commons.logging.LogFactory; 046 047import org.springframework.beans.factory.BeanFactoryUtils; 048import org.springframework.beans.factory.BeanInitializationException; 049import org.springframework.beans.factory.NoSuchBeanDefinitionException; 050import org.springframework.context.ApplicationContext; 051import org.springframework.core.annotation.AnnotationAwareOrderComparator; 052import org.springframework.core.io.ClassPathResource; 053import org.springframework.core.io.support.PropertiesLoaderUtils; 054import org.springframework.core.style.StylerUtils; 055import org.springframework.util.ClassUtils; 056import org.springframework.util.StringUtils; 057import org.springframework.web.multipart.MultipartException; 058import org.springframework.web.portlet.multipart.MultipartActionRequest; 059import org.springframework.web.portlet.multipart.PortletMultipartResolver; 060import org.springframework.web.servlet.View; 061import org.springframework.web.servlet.ViewRendererServlet; 062import org.springframework.web.servlet.ViewResolver; 063 064/** 065 * Central dispatcher for use within the Portlet MVC framework, e.g. for web UI 066 * controllers. Dispatches to registered handlers for processing a portlet request. 067 * 068 * <p>This portlet is very flexible: It can be used with just about any workflow, 069 * with the installation of the appropriate adapter classes. It offers the following 070 * functionality that distinguishes it from other request-driven Portlet MVC frameworks: 071 * 072 * <ul> 073 * <li>It is based around a JavaBeans configuration mechanism. 074 * 075 * <li>It can use any {@link HandlerMapping} implementation - pre-built or provided 076 * as part of an application - to control the routing of requests to handler objects. 077 * Default is a {@link org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping}. 078 * HandlerMapping objects can be defined as beans in the portlet's application context, 079 * implementing the HandlerMapping interface, overriding the default HandlerMapping 080 * if present. HandlerMappings can be given any bean name (they are tested by type). 081 * 082 * <li>It can use any {@link HandlerAdapter}; this allows for using any handler interface. 083 * The default adapter is {@link org.springframework.web.portlet.mvc.SimpleControllerHandlerAdapter} 084 * for Spring's {@link org.springframework.web.portlet.mvc.Controller} interface. 085 * A default {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter} 086 * will be registered as well. HandlerAdapter objects can be added as beans in the 087 * application context, overriding the default HandlerAdapter. Like HandlerMappings, 088 * HandlerAdapters can be given any bean name (they are tested by type). 089 * 090 * <li>The dispatcher's exception resolution strategy can be specified via a 091 * {@link HandlerExceptionResolver}, for example mapping certain exceptions to 092 * error pages. Default is none. Additional HandlerExceptionResolvers can be added 093 * through the application context. HandlerExceptionResolver can be given any 094 * bean name (they are tested by type). 095 * 096 * <li>Its view resolution strategy can be specified via a {@link ViewResolver} 097 * implementation, resolving symbolic view names into View objects. Default is 098 * {@link org.springframework.web.servlet.view.InternalResourceViewResolver}. 099 * ViewResolver objects can be added as beans in the application context, 100 * overriding the default ViewResolver. ViewResolvers can be given any bean name 101 * (they are tested by type). 102 * 103 * <li>The dispatcher's strategy for resolving multipart requests is determined by a 104 * {@link org.springframework.web.portlet.multipart.PortletMultipartResolver} 105 * implementation. An implementations for Apache Commons FileUpload is included: 106 * {@link org.springframework.web.portlet.multipart.CommonsPortletMultipartResolver}. 107 * The MultipartResolver bean name is "portletMultipartResolver"; default is none. 108 * </ul> 109 * 110 * <p><b>NOTE: The {@code @RequestMapping} annotation will only be processed if a 111 * corresponding {@code HandlerMapping} (for type-level annotations) and/or 112 * {@code HandlerAdapter} (for method-level annotations) is present in the dispatcher.</b> 113 * This is the case by default. However, if you are defining custom {@code HandlerMappings} 114 * or {@code HandlerAdapters}, then you need to make sure that a corresponding custom 115 * {@code DefaultAnnotationHandlerMapping} and/or {@code AnnotationMethodHandlerAdapter} 116 * is defined as well - provided that you intend to use {@code @RequestMapping}. 117 * 118 * <p><b>A web application can define any number of DispatcherPortlets.</b> 119 * Each portlet will operate in its own namespace, loading its own application context 120 * with mappings, handlers, etc. Only the root application context as loaded by 121 * {@link org.springframework.web.context.ContextLoaderListener}, if any, will be shared. 122 * 123 * <p>Thanks to Rainer Schmitz, Nick Lothian and Eric Dalquist for their suggestions! 124 * 125 * @author William G. Thompson, Jr. 126 * @author John A. Lewis 127 * @author Juergen Hoeller 128 * @since 2.0 129 * @see org.springframework.web.portlet.mvc.Controller 130 * @see org.springframework.web.servlet.ViewRendererServlet 131 * @see org.springframework.web.context.ContextLoaderListener 132 */ 133public class DispatcherPortlet extends FrameworkPortlet { 134 135 /** 136 * Well-known name for the PortletMultipartResolver object in the bean factory for this namespace. 137 */ 138 public static final String MULTIPART_RESOLVER_BEAN_NAME = "portletMultipartResolver"; 139 140 /** 141 * Well-known name for the HandlerMapping object in the bean factory for this namespace. 142 * Only used when "detectAllHandlerMappings" is turned off. 143 * @see #setDetectAllViewResolvers 144 */ 145 public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping"; 146 147 /** 148 * Well-known name for the HandlerAdapter object in the bean factory for this namespace. 149 * Only used when "detectAllHandlerAdapters" is turned off. 150 * @see #setDetectAllHandlerAdapters 151 */ 152 public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter"; 153 154 /** 155 * Well-known name for the HandlerExceptionResolver object in the bean factory for this 156 * namespace. Only used when "detectAllHandlerExceptionResolvers" is turned off. 157 * @see #setDetectAllViewResolvers 158 */ 159 public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver"; 160 161 /** 162 * Well-known name for the ViewResolver object in the bean factory for this namespace. 163 */ 164 public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver"; 165 166 /** 167 * Default URL to ViewRendererServlet. This bridge servlet is used to convert 168 * portlet render requests to servlet requests in order to leverage the view support 169 * in the {@code org.springframework.web.view} package. 170 */ 171 public static final String DEFAULT_VIEW_RENDERER_URL = "/WEB-INF/servlet/view"; 172 173 /** 174 * Unlike the Servlet version of this class, we have to deal with the 175 * two-phase nature of the portlet request. To do this, we need to pass 176 * forward any exception that occurs during the action phase, so that 177 * it can be displayed in the render phase. The only direct way to pass 178 * things forward and preserve them for each render request is through 179 * render parameters, but these are limited to String objects and we need 180 * to pass the Exception itself. The only other way to do this is in the 181 * session. The bad thing about using the session is that we have no way 182 * of knowing when we are done re-rendering the request and so we don't 183 * know when we can remove the objects from the session. So we will end 184 * up polluting the session with an old exception when we finally leave 185 * the render phase of one request and move on to something else. 186 */ 187 public static final String ACTION_EXCEPTION_SESSION_ATTRIBUTE = 188 DispatcherPortlet.class.getName() + ".ACTION_EXCEPTION"; 189 190 /** 191 * This render parameter is used to indicate forward to the render phase 192 * that an exception occurred during the action phase. 193 */ 194 public static final String ACTION_EXCEPTION_RENDER_PARAMETER = "actionException"; 195 196 /** 197 * Log category to use when no mapped handler is found for a request. 198 */ 199 public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.portlet.PageNotFound"; 200 201 /** 202 * Name of the class path resource (relative to the DispatcherPortlet class) 203 * that defines DispatcherPortet's default strategy names. 204 */ 205 private static final String DEFAULT_STRATEGIES_PATH = "DispatcherPortlet.properties"; 206 207 208 /** 209 * Additional logger to use when no mapped handler is found for a request. 210 */ 211 protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY); 212 213 private static final Properties defaultStrategies; 214 215 static { 216 // Load default strategy implementations from properties file. 217 // This is currently strictly internal and not meant to be customized 218 // by application developers. 219 try { 220 ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherPortlet.class); 221 defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); 222 } 223 catch (IOException ex) { 224 throw new IllegalStateException("Could not load 'DispatcherPortlet.properties': " + ex.getMessage()); 225 } 226 } 227 228 229 /** Detect all HandlerMappings or just expect "handlerMapping" bean? */ 230 private boolean detectAllHandlerMappings = true; 231 232 /** Detect all HandlerAdapters or just expect "handlerAdapter" bean? */ 233 private boolean detectAllHandlerAdapters = true; 234 235 /** Detect all HandlerExceptionResolvers or just expect "handlerExceptionResolver" bean? */ 236 private boolean detectAllHandlerExceptionResolvers = true; 237 238 /** Detect all ViewResolvers or just expect "viewResolver" bean? */ 239 private boolean detectAllViewResolvers = true; 240 241 /** Whether exceptions thrown during doAction should be forwarded to doRender */ 242 private boolean forwardActionException = true; 243 244 /** Whether exceptions thrown during doEvent should be forwarded to doRender */ 245 private boolean forwardEventException = false; 246 247 /** URL that points to the ViewRendererServlet */ 248 private String viewRendererUrl = DEFAULT_VIEW_RENDERER_URL; 249 250 251 /** MultipartResolver used by this portlet */ 252 private PortletMultipartResolver multipartResolver; 253 254 /** List of HandlerMappings used by this portlet */ 255 private List<HandlerMapping> handlerMappings; 256 257 /** List of HandlerAdapters used by this portlet */ 258 private List<HandlerAdapter> handlerAdapters; 259 260 /** List of HandlerExceptionResolvers used by this portlet */ 261 private List<HandlerExceptionResolver> handlerExceptionResolvers; 262 263 /** List of ViewResolvers used by this portlet */ 264 private List<ViewResolver> viewResolvers; 265 266 267 /** 268 * Set whether to detect all HandlerMapping beans in this portlet's context. 269 * Else, just a single bean with name "handlerMapping" will be expected. 270 * <p>Default is true. Turn this off if you want this portlet to use a 271 * single HandlerMapping, despite multiple HandlerMapping beans being 272 * defined in the context. 273 */ 274 public void setDetectAllHandlerMappings(boolean detectAllHandlerMappings) { 275 this.detectAllHandlerMappings = detectAllHandlerMappings; 276 } 277 278 /** 279 * Set whether to detect all HandlerAdapter beans in this portlet's context. 280 * Else, just a single bean with name "handlerAdapter" will be expected. 281 * <p>Default is "true". Turn this off if you want this portlet to use a 282 * single HandlerAdapter, despite multiple HandlerAdapter beans being 283 * defined in the context. 284 */ 285 public void setDetectAllHandlerAdapters(boolean detectAllHandlerAdapters) { 286 this.detectAllHandlerAdapters = detectAllHandlerAdapters; 287 } 288 289 /** 290 * Set whether to detect all HandlerExceptionResolver beans in this portlet's context. 291 * Else, just a single bean with name "handlerExceptionResolver" will be expected. 292 * <p>Default is true. Turn this off if you want this portlet to use a 293 * single HandlerExceptionResolver, despite multiple HandlerExceptionResolver 294 * beans being defined in the context. 295 */ 296 public void setDetectAllHandlerExceptionResolvers(boolean detectAllHandlerExceptionResolvers) { 297 this.detectAllHandlerExceptionResolvers = detectAllHandlerExceptionResolvers; 298 } 299 300 /** 301 * Set whether to detect all ViewResolver beans in this portlet's context. 302 * Else, just a single bean with name "viewResolver" will be expected. 303 * <p>Default is true. Turn this off if you want this portlet to use a 304 * single ViewResolver, despite multiple ViewResolver beans being 305 * defined in the context. 306 */ 307 public void setDetectAllViewResolvers(boolean detectAllViewResolvers) { 308 this.detectAllViewResolvers = detectAllViewResolvers; 309 } 310 311 /** 312 * Set whether to forward exceptions thrown during the action phase 313 * to the render phase via a session attribute. 314 * <p>Default is true. Turn this off if you want the portlet container 315 * to provide immediate exception handling for action requests. 316 * @see #exposeActionException(javax.portlet.PortletRequest, javax.portlet.StateAwareResponse, Exception) 317 */ 318 public void setForwardActionException(boolean forwardActionException) { 319 this.forwardActionException = forwardActionException; 320 } 321 322 /** 323 * Set whether to forward exceptions thrown during the event phase 324 * to the render phase via a session attribute. 325 * <p>Default is false. Turn this on if you want the {@link DispatcherPortlet} 326 * to forward the exception to the render phase, similar to what it does 327 * for {@link #setForwardActionException action exceptions} by default. 328 */ 329 public void setForwardEventException(boolean forwardEventException) { 330 this.forwardEventException = forwardEventException; 331 } 332 333 /** 334 * Set the URL to the ViewRendererServlet. That servlet is used to 335 * ultimately render all views in the portlet application. 336 */ 337 public void setViewRendererUrl(String viewRendererUrl) { 338 this.viewRendererUrl = viewRendererUrl; 339 } 340 341 342 /** 343 * This implementation calls {@link #initStrategies}. 344 */ 345 @Override 346 public void onRefresh(ApplicationContext context) { 347 initStrategies(context); 348 } 349 350 /** 351 * Refresh the strategy objects that this portlet uses. 352 * <p>May be overridden in subclasses in order to initialize 353 * further strategy objects. 354 */ 355 protected void initStrategies(ApplicationContext context) { 356 initMultipartResolver(context); 357 initHandlerMappings(context); 358 initHandlerAdapters(context); 359 initHandlerExceptionResolvers(context); 360 initViewResolvers(context); 361 } 362 363 /** 364 * Initialize the PortletMultipartResolver used by this class. 365 * <p>If no valid bean is defined with the given name in the BeanFactory 366 * for this namespace, no multipart handling is provided. 367 */ 368 private void initMultipartResolver(ApplicationContext context) { 369 try { 370 this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, PortletMultipartResolver.class); 371 if (logger.isDebugEnabled()) { 372 logger.debug("Using MultipartResolver [" + this.multipartResolver + "]"); 373 } 374 } 375 catch (NoSuchBeanDefinitionException ex) { 376 // Default is no multipart resolver. 377 this.multipartResolver = null; 378 if (logger.isDebugEnabled()) { 379 logger.debug("Unable to locate PortletMultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME + 380 "': no multipart request handling provided"); 381 } 382 } 383 } 384 385 /** 386 * Initialize the HandlerMappings used by this class. 387 * <p>If no HandlerMapping beans are defined in the BeanFactory 388 * for this namespace, we default to PortletModeHandlerMapping. 389 */ 390 private void initHandlerMappings(ApplicationContext context) { 391 this.handlerMappings = null; 392 393 if (this.detectAllHandlerMappings) { 394 // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. 395 Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( 396 context, HandlerMapping.class, true, false); 397 if (!matchingBeans.isEmpty()) { 398 this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values()); 399 // We keep HandlerMappings in sorted order. 400 AnnotationAwareOrderComparator.sort(this.handlerMappings); 401 } 402 } 403 else { 404 try { 405 HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); 406 this.handlerMappings = Collections.singletonList(hm); 407 } 408 catch (NoSuchBeanDefinitionException ex) { 409 // Ignore, we'll add a default HandlerMapping later. 410 } 411 } 412 413 // Ensure we have at least one HandlerMapping, by registering 414 // a default HandlerMapping if no other mappings are found. 415 if (this.handlerMappings == null) { 416 this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); 417 if (logger.isDebugEnabled()) { 418 logger.debug("No HandlerMappings found in portlet '" + getPortletName() + "': using default"); 419 } 420 } 421 } 422 423 /** 424 * Initialize the HandlerAdapters used by this class. 425 * <p>If no HandlerAdapter beans are defined in the BeanFactory 426 * for this namespace, we default to SimpleControllerHandlerAdapter. 427 */ 428 private void initHandlerAdapters(ApplicationContext context) { 429 this.handlerAdapters = null; 430 431 if (this.detectAllHandlerAdapters) { 432 // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts. 433 Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( 434 context, HandlerAdapter.class, true, false); 435 if (!matchingBeans.isEmpty()) { 436 this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values()); 437 // We keep HandlerAdapters in sorted order. 438 AnnotationAwareOrderComparator.sort(this.handlerAdapters); 439 } 440 } 441 else { 442 try { 443 HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class); 444 this.handlerAdapters = Collections.singletonList(ha); 445 } 446 catch (NoSuchBeanDefinitionException ex) { 447 // Ignore, we'll add a default HandlerAdapter later. 448 } 449 } 450 451 // Ensure we have at least some HandlerAdapters, by registering 452 // default HandlerAdapters if no other adapters are found. 453 if (this.handlerAdapters == null) { 454 this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class); 455 if (logger.isDebugEnabled()) { 456 logger.debug("No HandlerAdapters found in portlet '" + getPortletName() + "': using default"); 457 } 458 } 459 } 460 461 /** 462 * Initialize the HandlerExceptionResolver used by this class. 463 * <p>If no bean is defined with the given name in the BeanFactory 464 * for this namespace, we default to no exception resolver. 465 */ 466 private void initHandlerExceptionResolvers(ApplicationContext context) { 467 this.handlerExceptionResolvers = null; 468 469 if (this.detectAllHandlerExceptionResolvers) { 470 // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts. 471 Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( 472 context, HandlerExceptionResolver.class, true, false); 473 if (!matchingBeans.isEmpty()) { 474 this.handlerExceptionResolvers = new ArrayList<HandlerExceptionResolver>(matchingBeans.values()); 475 // We keep HandlerExceptionResolvers in sorted order. 476 AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers); 477 } 478 } 479 else { 480 try { 481 HandlerExceptionResolver her = context.getBean( 482 HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class); 483 this.handlerExceptionResolvers = Collections.singletonList(her); 484 } 485 catch (NoSuchBeanDefinitionException ex) { 486 // Ignore, no HandlerExceptionResolver is fine too. 487 } 488 } 489 490 // Just for consistency, check for default HandlerExceptionResolvers... 491 // There aren't any in usual scenarios. 492 if (this.handlerExceptionResolvers == null) { 493 this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class); 494 if (logger.isDebugEnabled()) { 495 logger.debug("No HandlerExceptionResolvers found in portlet '" + getPortletName() + "': using default"); 496 } 497 } 498 } 499 500 /** 501 * Initialize the ViewResolvers used by this class. 502 * <p>If no ViewResolver beans are defined in the BeanFactory 503 * for this namespace, we default to InternalResourceViewResolver. 504 */ 505 private void initViewResolvers(ApplicationContext context) { 506 this.viewResolvers = null; 507 508 if (this.detectAllViewResolvers) { 509 // Find all ViewResolvers in the ApplicationContext, including ancestor contexts. 510 Map<String, ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( 511 context, ViewResolver.class, true, false); 512 if (!matchingBeans.isEmpty()) { 513 this.viewResolvers = new ArrayList<ViewResolver>(matchingBeans.values()); 514 // We keep ViewResolvers in sorted order. 515 AnnotationAwareOrderComparator.sort(this.viewResolvers); 516 } 517 } 518 else { 519 try { 520 ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class); 521 this.viewResolvers = Collections.singletonList(vr); 522 } 523 catch (NoSuchBeanDefinitionException ex) { 524 // Ignore, we'll add a default ViewResolver later. 525 } 526 } 527 528 // Ensure we have at least one ViewResolver, by registering 529 // a default ViewResolver if no other resolvers are found. 530 if (this.viewResolvers == null) { 531 this.viewResolvers = getDefaultStrategies(context, ViewResolver.class); 532 if (logger.isDebugEnabled()) { 533 logger.debug("No ViewResolvers found in portlet '" + getPortletName() + "': using default"); 534 } 535 } 536 } 537 538 539 /** 540 * Return the default strategy object for the given strategy interface. 541 * <p>The default implementation delegates to {@link #getDefaultStrategies}, 542 * expecting a single object in the list. 543 * @param context the current Portlet ApplicationContext 544 * @param strategyInterface the strategy interface 545 * @return the corresponding strategy object 546 * @see #getDefaultStrategies 547 */ 548 protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) { 549 List<T> strategies = getDefaultStrategies(context, strategyInterface); 550 if (strategies.size() != 1) { 551 throw new BeanInitializationException( 552 "DispatcherPortlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]"); 553 } 554 return strategies.get(0); 555 } 556 557 /** 558 * Create a List of default strategy objects for the given strategy interface. 559 * <p>The default implementation uses the "DispatcherPortlet.properties" file 560 * (in the same package as the DispatcherPortlet class) to determine the class names. 561 * It instantiates the strategy objects and satisifies ApplicationContextAware 562 * if necessary. 563 * @param context the current Portlet ApplicationContext 564 * @param strategyInterface the strategy interface 565 * @return the List of corresponding strategy objects 566 */ 567 @SuppressWarnings("unchecked") 568 protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) { 569 String key = strategyInterface.getName(); 570 String value = defaultStrategies.getProperty(key); 571 if (value != null) { 572 String[] classNames = StringUtils.commaDelimitedListToStringArray(value); 573 List<T> strategies = new ArrayList<T>(classNames.length); 574 for (String className : classNames) { 575 try { 576 Class<?> clazz = ClassUtils.forName(className, DispatcherPortlet.class.getClassLoader()); 577 Object strategy = createDefaultStrategy(context, clazz); 578 strategies.add((T) strategy); 579 } 580 catch (ClassNotFoundException ex) { 581 throw new BeanInitializationException( 582 "Could not find DispatcherPortlet's default strategy class [" + className + 583 "] for interface [" + key + "]", ex); 584 } 585 catch (LinkageError err) { 586 throw new BeanInitializationException( 587 "Error loading DispatcherPortlet's default strategy class [" + className + 588 "] for interface [" + key + "]: problem with class file or dependent class", err); 589 } 590 } 591 return strategies; 592 } 593 else { 594 return new LinkedList<T>(); 595 } 596 } 597 598 /** 599 * Create a default strategy. 600 * <p>The default implementation uses 601 * {@link org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean}. 602 * @param context the current Portlet ApplicationContext 603 * @param clazz the strategy implementation class to instantiate 604 * @return the fully configured strategy instance 605 * @see org.springframework.context.ApplicationContext#getAutowireCapableBeanFactory() 606 */ 607 protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) { 608 return context.getAutowireCapableBeanFactory().createBean(clazz); 609 } 610 611 612 /** 613 * Obtain this portlet's PortletMultipartResolver, if any. 614 * @return the PortletMultipartResolver used by this portlet, or {@code null} 615 * if none (indicating that no multipart support is available) 616 */ 617 public PortletMultipartResolver getMultipartResolver() { 618 return this.multipartResolver; 619 } 620 621 622 /** 623 * Processes the actual dispatching to the handler for action requests. 624 * <p>The handler will be obtained by applying the portlet's HandlerMappings in order. 625 * The HandlerAdapter will be obtained by querying the portlet's installed 626 * HandlerAdapters to find the first that supports the handler class. 627 * @param request current portlet action request 628 * @param response current portlet Action response 629 * @throws Exception in case of any kind of processing failure 630 */ 631 @Override 632 protected void doActionService(ActionRequest request, ActionResponse response) throws Exception { 633 if (logger.isDebugEnabled()) { 634 logger.debug("DispatcherPortlet with name '" + getPortletName() + "' received action request"); 635 } 636 637 ActionRequest processedRequest = request; 638 HandlerExecutionChain mappedHandler = null; 639 int interceptorIndex = -1; 640 641 try { 642 processedRequest = checkMultipart(request); 643 644 // Determine handler for the current request. 645 mappedHandler = getHandler(processedRequest); 646 if (mappedHandler == null || mappedHandler.getHandler() == null) { 647 noHandlerFound(processedRequest, response); 648 return; 649 } 650 651 // Apply preHandle methods of registered interceptors. 652 HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); 653 if (interceptors != null) { 654 for (int i = 0; i < interceptors.length; i++) { 655 HandlerInterceptor interceptor = interceptors[i]; 656 if (!interceptor.preHandleAction(processedRequest, response, mappedHandler.getHandler())) { 657 triggerAfterActionCompletion(mappedHandler, interceptorIndex, processedRequest, response, null); 658 return; 659 } 660 interceptorIndex = i; 661 } 662 } 663 664 // Actually invoke the handler. 665 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 666 ha.handleAction(processedRequest, response, mappedHandler.getHandler()); 667 668 // Trigger after-completion for successful outcome. 669 triggerAfterActionCompletion(mappedHandler, interceptorIndex, processedRequest, response, null); 670 } 671 672 catch (Exception ex) { 673 // Trigger after-completion for thrown exception. 674 triggerAfterActionCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex); 675 // Forward the exception to the render phase to be displayed. 676 if (this.forwardActionException) { 677 try { 678 exposeActionException(request, response, ex); 679 logger.debug("Caught exception during action phase - forwarding to render phase", ex); 680 } 681 catch (IllegalStateException ex2) { 682 // Probably sendRedirect called... need to rethrow exception immediately. 683 throw ex; 684 } 685 } 686 else { 687 throw ex; 688 } 689 } 690 catch (Throwable err) { 691 PortletException ex = 692 new PortletException("Error occured during request processing: " + err.getMessage(), err); 693 // Trigger after-completion for thrown exception. 694 triggerAfterActionCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex); 695 throw ex; 696 } 697 698 finally { 699 // Clean up any resources used by a multipart request. 700 if (processedRequest instanceof MultipartActionRequest && processedRequest != request) { 701 this.multipartResolver.cleanupMultipart((MultipartActionRequest) processedRequest); 702 } 703 } 704 } 705 706 /** 707 * Processes the actual dispatching to the handler for render requests. 708 * <p>The handler will be obtained by applying the portlet's HandlerMappings in order. 709 * The HandlerAdapter will be obtained by querying the portlet's installed 710 * HandlerAdapters to find the first that supports the handler class. 711 * @param request current portlet render request 712 * @param response current portlet render response 713 * @throws Exception in case of any kind of processing failure 714 */ 715 @Override 716 protected void doRenderService(RenderRequest request, RenderResponse response) throws Exception { 717 if (logger.isDebugEnabled()) { 718 logger.debug("DispatcherPortlet with name '" + getPortletName() + "' received render request"); 719 } 720 721 HandlerExecutionChain mappedHandler = null; 722 int interceptorIndex = -1; 723 724 try { 725 ModelAndView mv; 726 try { 727 // Determine handler for the current request. 728 mappedHandler = getHandler(request); 729 if (mappedHandler == null || mappedHandler.getHandler() == null) { 730 noHandlerFound(request, response); 731 return; 732 } 733 734 // Apply preHandle methods of registered interceptors. 735 HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); 736 if (interceptors != null) { 737 for (int i = 0; i < interceptors.length; i++) { 738 HandlerInterceptor interceptor = interceptors[i]; 739 if (!interceptor.preHandleRender(request, response, mappedHandler.getHandler())) { 740 triggerAfterRenderCompletion(mappedHandler, interceptorIndex, request, response, null); 741 return; 742 } 743 interceptorIndex = i; 744 } 745 } 746 747 // Check for forwarded exception from the action phase 748 PortletSession session = request.getPortletSession(false); 749 if (session != null) { 750 if (request.getParameter(ACTION_EXCEPTION_RENDER_PARAMETER) != null) { 751 Exception ex = (Exception) session.getAttribute(ACTION_EXCEPTION_SESSION_ATTRIBUTE); 752 if (ex != null) { 753 logger.debug("Render phase found exception caught during action phase - rethrowing it"); 754 throw ex; 755 } 756 } 757 else { 758 session.removeAttribute(ACTION_EXCEPTION_SESSION_ATTRIBUTE); 759 } 760 } 761 762 // Actually invoke the handler. 763 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 764 mv = ha.handleRender(request, response, mappedHandler.getHandler()); 765 766 // Apply postHandle methods of registered interceptors. 767 if (interceptors != null) { 768 for (int i = interceptors.length - 1; i >= 0; i--) { 769 HandlerInterceptor interceptor = interceptors[i]; 770 interceptor.postHandleRender(request, response, mappedHandler.getHandler(), mv); 771 } 772 } 773 } 774 catch (ModelAndViewDefiningException ex) { 775 logger.debug("ModelAndViewDefiningException encountered", ex); 776 mv = ex.getModelAndView(); 777 } 778 catch (Exception ex) { 779 Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); 780 mv = processHandlerException(request, response, handler, ex); 781 } 782 783 // Did the handler return a view to render? 784 if (mv != null && !mv.isEmpty()) { 785 render(mv, request, response); 786 } 787 else { 788 if (logger.isDebugEnabled()) { 789 logger.debug("Null ModelAndView returned to DispatcherPortlet with name '" + 790 getPortletName() + "': assuming HandlerAdapter completed request handling"); 791 } 792 } 793 794 // Trigger after-completion for successful outcome. 795 triggerAfterRenderCompletion(mappedHandler, interceptorIndex, request, response, null); 796 } 797 798 catch (Exception ex) { 799 // Trigger after-completion for thrown exception. 800 triggerAfterRenderCompletion(mappedHandler, interceptorIndex, request, response, ex); 801 throw ex; 802 } 803 catch (Throwable err) { 804 PortletException ex = 805 new PortletException("Error occured during request processing: " + err.getMessage(), err); 806 // Trigger after-completion for thrown exception. 807 triggerAfterRenderCompletion(mappedHandler, interceptorIndex, request, response, ex); 808 throw ex; 809 } 810 } 811 812 /** 813 * Processes the actual dispatching to the handler for resource requests. 814 * <p>The handler will be obtained by applying the portlet's HandlerMappings in order. 815 * The HandlerAdapter will be obtained by querying the portlet's installed 816 * HandlerAdapters to find the first that supports the handler class. 817 * @param request current portlet render request 818 * @param response current portlet render response 819 * @throws Exception in case of any kind of processing failure 820 */ 821 @Override 822 protected void doResourceService(ResourceRequest request, ResourceResponse response) throws Exception { 823 if (logger.isDebugEnabled()) { 824 logger.debug("DispatcherPortlet with name '" + getPortletName() + "' received resource request"); 825 } 826 827 HandlerExecutionChain mappedHandler = null; 828 int interceptorIndex = -1; 829 830 try { 831 ModelAndView mv; 832 try { 833 // Determine handler for the current request. 834 mappedHandler = getHandler(request); 835 if (mappedHandler == null || mappedHandler.getHandler() == null) { 836 noHandlerFound(request, response); 837 return; 838 } 839 840 // Apply preHandle methods of registered interceptors. 841 HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); 842 if (interceptors != null) { 843 for (int i = 0; i < interceptors.length; i++) { 844 HandlerInterceptor interceptor = interceptors[i]; 845 if (!interceptor.preHandleResource(request, response, mappedHandler.getHandler())) { 846 triggerAfterResourceCompletion(mappedHandler, interceptorIndex, request, response, null); 847 return; 848 } 849 interceptorIndex = i; 850 } 851 } 852 853 // Actually invoke the handler. 854 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 855 mv = ha.handleResource(request, response, mappedHandler.getHandler()); 856 857 // Apply postHandle methods of registered interceptors. 858 if (interceptors != null) { 859 for (int i = interceptors.length - 1; i >= 0; i--) { 860 HandlerInterceptor interceptor = interceptors[i]; 861 interceptor.postHandleResource(request, response, mappedHandler.getHandler(), mv); 862 } 863 } 864 } 865 catch (ModelAndViewDefiningException ex) { 866 logger.debug("ModelAndViewDefiningException encountered", ex); 867 mv = ex.getModelAndView(); 868 } 869 catch (Exception ex) { 870 Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); 871 mv = processHandlerException(request, response, handler, ex); 872 } 873 874 // Did the handler return a view to render? 875 if (mv != null && !mv.isEmpty()) { 876 render(mv, request, response); 877 } 878 else { 879 if (logger.isDebugEnabled()) { 880 logger.debug("Null ModelAndView returned to DispatcherPortlet with name '" + 881 getPortletName() + "': assuming HandlerAdapter completed request handling"); 882 } 883 } 884 885 // Trigger after-completion for successful outcome. 886 triggerAfterResourceCompletion(mappedHandler, interceptorIndex, request, response, null); 887 } 888 889 catch (Exception ex) { 890 // Trigger after-completion for thrown exception. 891 triggerAfterResourceCompletion(mappedHandler, interceptorIndex, request, response, ex); 892 throw ex; 893 } 894 catch (Throwable err) { 895 PortletException ex = 896 new PortletException("Error occured during request processing: " + err.getMessage(), err); 897 // Trigger after-completion for thrown exception. 898 triggerAfterResourceCompletion(mappedHandler, interceptorIndex, request, response, ex); 899 throw ex; 900 } 901 } 902 903 /** 904 * Processes the actual dispatching to the handler for event requests. 905 * <p>The handler will be obtained by applying the portlet's HandlerMappings in order. 906 * The HandlerAdapter will be obtained by querying the portlet's installed 907 * HandlerAdapters to find the first that supports the handler class. 908 * @param request current portlet action request 909 * @param response current portlet Action response 910 * @throws Exception in case of any kind of processing failure 911 */ 912 @Override 913 protected void doEventService(EventRequest request, EventResponse response) throws Exception { 914 if (logger.isDebugEnabled()) { 915 logger.debug("DispatcherPortlet with name '" + getPortletName() + "' received action request"); 916 } 917 918 HandlerExecutionChain mappedHandler = null; 919 int interceptorIndex = -1; 920 921 try { 922 // Determine handler for the current request. 923 mappedHandler = getHandler(request); 924 if (mappedHandler == null || mappedHandler.getHandler() == null) { 925 noHandlerFound(request, response); 926 return; 927 } 928 929 // Apply preHandle methods of registered interceptors. 930 HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); 931 if (interceptors != null) { 932 for (int i = 0; i < interceptors.length; i++) { 933 HandlerInterceptor interceptor = interceptors[i]; 934 if (!interceptor.preHandleEvent(request, response, mappedHandler.getHandler())) { 935 triggerAfterEventCompletion(mappedHandler, interceptorIndex, request, response, null); 936 return; 937 } 938 interceptorIndex = i; 939 } 940 } 941 942 // Actually invoke the handler. 943 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 944 ha.handleEvent(request, response, mappedHandler.getHandler()); 945 946 // Trigger after-completion for successful outcome. 947 triggerAfterEventCompletion(mappedHandler, interceptorIndex, request, response, null); 948 } 949 950 catch (Exception ex) { 951 // Trigger after-completion for thrown exception. 952 triggerAfterEventCompletion(mappedHandler, interceptorIndex, request, response, ex); 953 // Forward the exception to the render phase to be displayed. 954 if (this.forwardEventException) { 955 try { 956 exposeActionException(request, response, ex); 957 logger.debug("Caught exception during event phase - forwarding to render phase", ex); 958 } 959 catch (IllegalStateException ex2) { 960 // Probably sendRedirect called... need to rethrow exception immediately. 961 throw ex; 962 } 963 } 964 else { 965 throw ex; 966 } 967 } 968 catch (Throwable err) { 969 PortletException ex = 970 new PortletException("Error occured during request processing: " + err.getMessage(), err); 971 // Trigger after-completion for thrown exception. 972 triggerAfterEventCompletion(mappedHandler, interceptorIndex, request, response, ex); 973 throw ex; 974 } 975 } 976 977 978 /** 979 * Convert the request into a multipart request, and make multipart resolver available. 980 * If no multipart resolver is set, simply use the existing request. 981 * @param request current HTTP request 982 * @return the processed request (multipart wrapper if necessary) 983 */ 984 protected ActionRequest checkMultipart(ActionRequest request) throws MultipartException { 985 if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) { 986 if (request instanceof MultipartActionRequest) { 987 logger.debug("Request is already a MultipartActionRequest - probably in a forward"); 988 } 989 else { 990 return this.multipartResolver.resolveMultipart(request); 991 } 992 } 993 // If not returned before: return original request. 994 return request; 995 } 996 997 /** 998 * Return the HandlerExecutionChain for this request. 999 * Try all handler mappings in order. 1000 * @param request current portlet request 1001 * @return the HandlerExecutionChain, or null if no handler could be found 1002 */ 1003 protected HandlerExecutionChain getHandler(PortletRequest request) throws Exception { 1004 for (HandlerMapping hm : this.handlerMappings) { 1005 if (logger.isDebugEnabled()) { 1006 logger.debug( 1007 "Testing handler map [" + hm + "] in DispatcherPortlet with name '" + getPortletName() + "'"); 1008 } 1009 HandlerExecutionChain handler = hm.getHandler(request); 1010 if (handler != null) { 1011 return handler; 1012 } 1013 } 1014 return null; 1015 } 1016 1017 /** 1018 * No handler found -> throw appropriate exception. 1019 * @param request current portlet request 1020 * @param response current portlet response 1021 * @throws Exception if preparing the response failed 1022 */ 1023 protected void noHandlerFound(PortletRequest request, PortletResponse response) throws Exception { 1024 if (pageNotFoundLogger.isWarnEnabled()) { 1025 pageNotFoundLogger.warn("No handler found for current request " + 1026 "in DispatcherPortlet with name '" + getPortletName() + 1027 "', mode '" + request.getPortletMode() + 1028 "', phase '" + request.getAttribute(PortletRequest.LIFECYCLE_PHASE) + 1029 "', parameters " + StylerUtils.style(request.getParameterMap())); 1030 } 1031 throw new NoHandlerFoundException("No handler found for portlet request", request); 1032 } 1033 1034 /** 1035 * Return the HandlerAdapter for this handler object. 1036 * @param handler the handler object to find an adapter for 1037 * @throws PortletException if no HandlerAdapter can be found for the handler. 1038 * This is a fatal error. 1039 */ 1040 protected HandlerAdapter getHandlerAdapter(Object handler) throws PortletException { 1041 for (HandlerAdapter ha : this.handlerAdapters) { 1042 if (logger.isDebugEnabled()) { 1043 logger.debug("Testing handler adapter [" + ha + "]"); 1044 } 1045 if (ha.supports(handler)) { 1046 return ha; 1047 } 1048 } 1049 throw new PortletException("No adapter for handler [" + handler + 1050 "]: Does your handler implement a supported interface like Controller?"); 1051 } 1052 1053 /** 1054 * Expose the given action exception to the given response. 1055 * @param request current portlet request 1056 * @param response current portlet response 1057 * @param ex the action exception (may also come from an event phase) 1058 */ 1059 protected void exposeActionException(PortletRequest request, StateAwareResponse response, Exception ex) { 1060 // Copy all parameters unless overridden in the action handler. 1061 Enumeration<String> paramNames = request.getParameterNames(); 1062 while (paramNames.hasMoreElements()) { 1063 String paramName = paramNames.nextElement(); 1064 String[] paramValues = request.getParameterValues(paramName); 1065 if (paramValues != null && !response.getRenderParameterMap().containsKey(paramName)) { 1066 response.setRenderParameter(paramName, paramValues); 1067 } 1068 } 1069 response.setRenderParameter(ACTION_EXCEPTION_RENDER_PARAMETER, ex.toString()); 1070 request.getPortletSession().setAttribute(ACTION_EXCEPTION_SESSION_ATTRIBUTE, ex); 1071 } 1072 1073 1074 /** 1075 * Render the given ModelAndView. This is the last stage in handling a request. 1076 * It may involve resolving the view by name. 1077 * @param mv the ModelAndView to render 1078 * @param request current portlet render request 1079 * @param response current portlet render response 1080 * @throws Exception if there's a problem rendering the view 1081 */ 1082 protected void render(ModelAndView mv, PortletRequest request, MimeResponse response) throws Exception { 1083 View view; 1084 if (mv.isReference()) { 1085 // We need to resolve the view name. 1086 view = resolveViewName(mv.getViewName(), mv.getModelInternal(), request); 1087 if (view == null) { 1088 throw new PortletException("Could not resolve view with name '" + mv.getViewName() + 1089 "' in portlet with name '" + getPortletName() + "'"); 1090 } 1091 } 1092 else { 1093 // No need to lookup: the ModelAndView object contains the actual View object. 1094 Object viewObject = mv.getView(); 1095 if (viewObject == null) { 1096 throw new PortletException("ModelAndView [" + mv + "] neither contains a view name nor a " + 1097 "View object in portlet with name '" + getPortletName() + "'"); 1098 } 1099 if (!(viewObject instanceof View)) { 1100 throw new PortletException( 1101 "View object [" + viewObject + "] is not an instance of [org.springframework.web.servlet.View] - " + 1102 "DispatcherPortlet does not support any other view types"); 1103 } 1104 view = (View) viewObject; 1105 } 1106 1107 // Set the content type on the response if needed and if possible. 1108 // The Portlet spec requires the content type to be set on the RenderResponse; 1109 // it's not sufficient to let the View set it on the ServletResponse. 1110 if (response.getContentType() != null) { 1111 if (logger.isDebugEnabled()) { 1112 logger.debug("Portlet response content type already set to [" + response.getContentType() + "]"); 1113 } 1114 } 1115 else { 1116 // No Portlet content type specified yet -> use the view-determined type. 1117 String contentType = view.getContentType(); 1118 if (contentType != null) { 1119 if (logger.isDebugEnabled()) { 1120 logger.debug("Setting portlet response content type to view-determined type [" + contentType + "]"); 1121 } 1122 response.setContentType(contentType); 1123 } 1124 } 1125 1126 doRender(view, mv.getModelInternal(), request, response); 1127 } 1128 1129 /** 1130 * Resolve the given view name into a View object (to be rendered). 1131 * <p>Default implementations asks all ViewResolvers of this dispatcher. 1132 * Can be overridden for custom resolution strategies, potentially based 1133 * on specific model attributes or request parameters. 1134 * @param viewName the name of the view to resolve 1135 * @param model the model to be passed to the view 1136 * @param request current portlet render request 1137 * @return the View object, or null if none found 1138 * @throws Exception if the view cannot be resolved 1139 * (typically in case of problems creating an actual View object) 1140 * @see ViewResolver#resolveViewName 1141 */ 1142 protected View resolveViewName(String viewName, Map<String, ?> model, PortletRequest request) throws Exception { 1143 for (ViewResolver viewResolver : this.viewResolvers) { 1144 View view = viewResolver.resolveViewName(viewName, request.getLocale()); 1145 if (view != null) { 1146 return view; 1147 } 1148 } 1149 return null; 1150 } 1151 1152 /** 1153 * Actually render the given view. 1154 * <p>The default implementation delegates to 1155 * {@link org.springframework.web.servlet.ViewRendererServlet}. 1156 * @param view the View to render 1157 * @param model the associated model 1158 * @param request current portlet render/resource request 1159 * @param response current portlet render/resource response 1160 * @throws Exception if there's a problem rendering the view 1161 */ 1162 protected void doRender(View view, Map<String, ?> model, PortletRequest request, MimeResponse response) throws Exception { 1163 // Expose Portlet ApplicationContext to view objects. 1164 request.setAttribute(ViewRendererServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE, getPortletApplicationContext()); 1165 1166 // These attributes are required by the ViewRendererServlet. 1167 request.setAttribute(ViewRendererServlet.VIEW_ATTRIBUTE, view); 1168 request.setAttribute(ViewRendererServlet.MODEL_ATTRIBUTE, model); 1169 1170 // Include the content of the view in the render/resource response. 1171 doDispatch(getPortletContext().getRequestDispatcher(this.viewRendererUrl), request, response); 1172 } 1173 1174 /** 1175 * Perform a dispatch on the given PortletRequestDispatcher. 1176 * <p>The default implementation uses a forward for resource requests 1177 * and an include for render requests. 1178 * @param dispatcher the PortletRequestDispatcher to use 1179 * @param request current portlet render/resource request 1180 * @param response current portlet render/resource response 1181 * @throws Exception if there's a problem performing the dispatch 1182 */ 1183 protected void doDispatch(PortletRequestDispatcher dispatcher, PortletRequest request, MimeResponse response) 1184 throws Exception { 1185 1186 // In general, we prefer a forward for resource responses, in order to have full Servlet API 1187 // support in the target resource (e.g. on uPortal). However, on Liferay, a resource forward 1188 // displays an empty page, so we have to resort to an include there... 1189 if (PortletRequest.RESOURCE_PHASE.equals(request.getAttribute(PortletRequest.LIFECYCLE_PHASE)) && 1190 !dispatcher.getClass().getName().startsWith("com.liferay")) { 1191 dispatcher.forward(request, response); 1192 } 1193 else { 1194 dispatcher.include(request, response); 1195 } 1196 } 1197 1198 1199 /** 1200 * Determine an error ModelAndView via the registered HandlerExceptionResolvers. 1201 * @param request current portlet request 1202 * @param response current portlet response 1203 * @param handler the executed handler, or null if none chosen at the time of 1204 * the exception (for example, if multipart resolution failed) 1205 * @param ex the exception that got thrown during handler execution 1206 * @return a corresponding ModelAndView to forward to 1207 * @throws Exception if no error ModelAndView found 1208 */ 1209 protected ModelAndView processHandlerException( 1210 RenderRequest request, RenderResponse response, Object handler, Exception ex) 1211 throws Exception { 1212 1213 ModelAndView exMv = null; 1214 for (Iterator<HandlerExceptionResolver> it = this.handlerExceptionResolvers.iterator(); exMv == null && it.hasNext();) { 1215 HandlerExceptionResolver resolver = it.next(); 1216 exMv = resolver.resolveException(request, response, handler, ex); 1217 } 1218 if (exMv != null) { 1219 if (logger.isDebugEnabled()) { 1220 logger.debug("HandlerExceptionResolver returned ModelAndView [" + exMv + "] for exception"); 1221 } 1222 logger.warn("Handler execution resulted in exception - forwarding to resolved error view", ex); 1223 return exMv; 1224 } 1225 else { 1226 throw ex; 1227 } 1228 } 1229 1230 /** 1231 * Determine an error ModelAndView via the registered HandlerExceptionResolvers. 1232 * @param request current portlet request 1233 * @param response current portlet response 1234 * @param handler the executed handler, or null if none chosen at the time of 1235 * the exception (for example, if multipart resolution failed) 1236 * @param ex the exception that got thrown during handler execution 1237 * @return a corresponding ModelAndView to forward to 1238 * @throws Exception if no error ModelAndView found 1239 */ 1240 protected ModelAndView processHandlerException( 1241 ResourceRequest request, ResourceResponse response, Object handler, Exception ex) 1242 throws Exception { 1243 1244 ModelAndView exMv = null; 1245 for (Iterator<HandlerExceptionResolver> it = this.handlerExceptionResolvers.iterator(); exMv == null && it.hasNext();) { 1246 HandlerExceptionResolver resolver = it.next(); 1247 exMv = resolver.resolveException(request, response, handler, ex); 1248 } 1249 if (exMv != null) { 1250 if (logger.isDebugEnabled()) { 1251 logger.debug("HandlerExceptionResolver returned ModelAndView [" + exMv + "] for exception"); 1252 } 1253 logger.warn("Handler execution resulted in exception - forwarding to resolved error view", ex); 1254 return exMv; 1255 } 1256 else { 1257 throw ex; 1258 } 1259 } 1260 1261 /** 1262 * Trigger afterCompletion callbacks on the mapped HandlerInterceptors. 1263 * Will just invoke afterCompletion for all interceptors whose preHandle 1264 * invocation has successfully completed and returned true. 1265 * @param mappedHandler the mapped HandlerExecutionChain 1266 * @param interceptorIndex index of last interceptor that successfully completed 1267 * @param ex Exception thrown on handler execution, or null if none 1268 * @see HandlerInterceptor#afterRenderCompletion 1269 */ 1270 private void triggerAfterActionCompletion(HandlerExecutionChain mappedHandler, int interceptorIndex, 1271 ActionRequest request, ActionResponse response, Exception ex) 1272 throws Exception { 1273 1274 // Apply afterCompletion methods of registered interceptors. 1275 if (mappedHandler != null) { 1276 HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); 1277 if (interceptors != null) { 1278 for (int i = interceptorIndex; i >= 0; i--) { 1279 HandlerInterceptor interceptor = interceptors[i]; 1280 try { 1281 interceptor.afterActionCompletion(request, response, mappedHandler.getHandler(), ex); 1282 } 1283 catch (Throwable ex2) { 1284 logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); 1285 } 1286 } 1287 } 1288 } 1289 } 1290 1291 /** 1292 * Trigger afterCompletion callbacks on the mapped HandlerInterceptors. 1293 * Will just invoke afterCompletion for all interceptors whose preHandle 1294 * invocation has successfully completed and returned true. 1295 * @param mappedHandler the mapped HandlerExecutionChain 1296 * @param interceptorIndex index of last interceptor that successfully completed 1297 * @param ex Exception thrown on handler execution, or null if none 1298 * @see HandlerInterceptor#afterRenderCompletion 1299 */ 1300 private void triggerAfterRenderCompletion(HandlerExecutionChain mappedHandler, int interceptorIndex, 1301 RenderRequest request, RenderResponse response, Exception ex) 1302 throws Exception { 1303 1304 // Apply afterCompletion methods of registered interceptors. 1305 if (mappedHandler != null) { 1306 HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); 1307 if (interceptors != null) { 1308 for (int i = interceptorIndex; i >= 0; i--) { 1309 HandlerInterceptor interceptor = interceptors[i]; 1310 try { 1311 interceptor.afterRenderCompletion(request, response, mappedHandler.getHandler(), ex); 1312 } 1313 catch (Throwable ex2) { 1314 logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); 1315 } 1316 } 1317 } 1318 } 1319 } 1320 1321 /** 1322 * Trigger afterCompletion callbacks on the mapped HandlerInterceptors. 1323 * Will just invoke afterCompletion for all interceptors whose preHandle 1324 * invocation has successfully completed and returned true. 1325 * @param mappedHandler the mapped HandlerExecutionChain 1326 * @param interceptorIndex index of last interceptor that successfully completed 1327 * @param ex Exception thrown on handler execution, or null if none 1328 * @see HandlerInterceptor#afterRenderCompletion 1329 */ 1330 private void triggerAfterResourceCompletion(HandlerExecutionChain mappedHandler, int interceptorIndex, 1331 ResourceRequest request, ResourceResponse response, Exception ex) 1332 throws Exception { 1333 1334 // Apply afterCompletion methods of registered interceptors. 1335 if (mappedHandler != null) { 1336 HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); 1337 if (interceptors != null) { 1338 for (int i = interceptorIndex; i >= 0; i--) { 1339 HandlerInterceptor interceptor = interceptors[i]; 1340 try { 1341 interceptor.afterResourceCompletion(request, response, mappedHandler.getHandler(), ex); 1342 } 1343 catch (Throwable ex2) { 1344 logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); 1345 } 1346 } 1347 } 1348 } 1349 } 1350 1351 /** 1352 * Trigger afterCompletion callbacks on the mapped HandlerInterceptors. 1353 * Will just invoke afterCompletion for all interceptors whose preHandle 1354 * invocation has successfully completed and returned true. 1355 * @param mappedHandler the mapped HandlerExecutionChain 1356 * @param interceptorIndex index of last interceptor that successfully completed 1357 * @param ex Exception thrown on handler execution, or null if none 1358 * @see HandlerInterceptor#afterRenderCompletion 1359 */ 1360 private void triggerAfterEventCompletion(HandlerExecutionChain mappedHandler, int interceptorIndex, 1361 EventRequest request, EventResponse response, Exception ex) 1362 throws Exception { 1363 1364 // Apply afterCompletion methods of registered interceptors. 1365 if (mappedHandler != null) { 1366 HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); 1367 if (interceptors != null) { 1368 for (int i = interceptorIndex; i >= 0; i--) { 1369 HandlerInterceptor interceptor = interceptors[i]; 1370 try { 1371 interceptor.afterEventCompletion(request, response, mappedHandler.getHandler(), ex); 1372 } 1373 catch (Throwable ex2) { 1374 logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); 1375 } 1376 } 1377 } 1378 } 1379 } 1380 1381}