0001: /*
0002: * Copyright 2002-2007 the original author or authors.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: package org.springframework.web.portlet;
0018:
0019: import java.io.IOException;
0020: import java.util.ArrayList;
0021: import java.util.Collections;
0022: import java.util.Iterator;
0023: import java.util.List;
0024: import java.util.Map;
0025: import java.util.Properties;
0026:
0027: import javax.portlet.ActionRequest;
0028: import javax.portlet.ActionResponse;
0029: import javax.portlet.PortletException;
0030: import javax.portlet.PortletRequest;
0031: import javax.portlet.PortletResponse;
0032: import javax.portlet.PortletSession;
0033: import javax.portlet.RenderRequest;
0034: import javax.portlet.RenderResponse;
0035: import javax.portlet.UnavailableException;
0036:
0037: import org.apache.commons.logging.Log;
0038: import org.apache.commons.logging.LogFactory;
0039:
0040: import org.springframework.beans.BeansException;
0041: import org.springframework.beans.factory.BeanFactoryUtils;
0042: import org.springframework.beans.factory.BeanInitializationException;
0043: import org.springframework.beans.factory.NoSuchBeanDefinitionException;
0044: import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
0045: import org.springframework.context.ApplicationContext;
0046: import org.springframework.context.i18n.LocaleContext;
0047: import org.springframework.context.i18n.LocaleContextHolder;
0048: import org.springframework.context.i18n.SimpleLocaleContext;
0049: import org.springframework.core.OrderComparator;
0050: import org.springframework.core.io.ClassPathResource;
0051: import org.springframework.core.io.support.PropertiesLoaderUtils;
0052: import org.springframework.util.ClassUtils;
0053: import org.springframework.util.StringUtils;
0054: import org.springframework.web.context.request.RequestAttributes;
0055: import org.springframework.web.context.request.RequestContextHolder;
0056: import org.springframework.web.multipart.MultipartException;
0057: import org.springframework.web.portlet.context.PortletRequestAttributes;
0058: import org.springframework.web.portlet.multipart.MultipartActionRequest;
0059: import org.springframework.web.portlet.multipart.PortletMultipartResolver;
0060: import org.springframework.web.servlet.View;
0061: import org.springframework.web.servlet.ViewRendererServlet;
0062: import org.springframework.web.servlet.ViewResolver;
0063:
0064: /**
0065: * Central dispatcher for use within the Portlet MVC framework, e.g. for web UI
0066: * controllers. Dispatches to registered handlers for processing a portlet request.
0067: *
0068: * <p>This portlet is very flexible: It can be used with just about any workflow,
0069: * with the installation of the appropriate adapter classes. It offers the
0070: * following functionality that distinguishes it from other request-driven
0071: * portlet MVC frameworks:
0072: *
0073: * <ul>
0074: * <li>It is based around a JavaBeans configuration mechanism.
0075: *
0076: * <li>It can use any {@link HandlerMapping} implementation - pre-built or provided
0077: * as part of an application - to control the routing of requests to handler objects.
0078: * Default is {@link org.springframework.web.portlet.handler.PortletModeHandlerMapping}.
0079: * HandlerMapping objects can be defined as beans in the portlet's application context,
0080: * implementing the HandlerMapping interface, overriding the default HandlerMapping
0081: * if present. HandlerMappings can be given any bean name (they are tested by type).
0082: *
0083: * <li>It can use any {@link HandlerAdapter}; this allows to use any handler interface.
0084: * The default adapter is {@link org.springframework.web.portlet.mvc.SimpleControllerHandlerAdapter}
0085: * for Spring's {@link org.springframework.web.portlet.mvc.Controller} interface.
0086: * HandlerAdapter objects can be added as beans in the application context,
0087: * overriding the default HandlerAdapter. Like HandlerMappings, HandlerAdapters
0088: * can be given any bean name (they are tested by type).
0089: *
0090: * <li>The dispatcher's exception resolution strategy can be specified via a
0091: * {@link HandlerExceptionResolver}, for example mapping certain exceptions to
0092: * error pages. Default is none. Additional HandlerExceptionResolvers can be added
0093: * through the application context. HandlerExceptionResolver can be given any
0094: * bean name (they are tested by type).
0095: *
0096: * <li>Its view resolution strategy can be specified via a {@link ViewResolver}
0097: * implementation, resolving symbolic view names into View objects. Default is
0098: * {@link org.springframework.web.servlet.view.InternalResourceViewResolver}.
0099: * ViewResolver objects can be added as beans in the application context,
0100: * overriding the default ViewResolver. ViewResolvers can be given any bean name
0101: * (they are tested by type).
0102: *
0103: * <li>The dispatcher's strategy for resolving multipart requests is determined by a
0104: * {@link org.springframework.web.portlet.multipart.PortletMultipartResolver} implementation.
0105: * An implementations for Jakarta Commons FileUpload is included:
0106: * {@link org.springframework.web.portlet.multipart.CommonsPortletMultipartResolver}.
0107: * The MultipartResolver bean name is "portletMultipartResolver"; default is none.
0108: * </ul>
0109: *
0110: * <p><b>A web application can define any number of DispatcherPortlets.</b>
0111: * Each portlet will operate in its own namespace, loading its own application
0112: * context with mappings, handlers, etc. Only the root application context
0113: * as loaded by {@link org.springframework.web.context.ContextLoaderListener},
0114: * if any, will be shared.
0115: *
0116: * <p>Thanks to Rainer Schmitz and Nick Lothian for their suggestions!
0117: *
0118: * @author William G. Thompson, Jr.
0119: * @author John A. Lewis
0120: * @author Juergen Hoeller
0121: * @since 2.0
0122: * @see org.springframework.web.portlet.mvc.Controller
0123: * @see org.springframework.web.servlet.ViewRendererServlet
0124: * @see org.springframework.web.context.ContextLoaderListener
0125: */
0126: public class DispatcherPortlet extends FrameworkPortlet {
0127:
0128: /**
0129: * Well-known name for the PortletMultipartResolver object in the bean factory for this namespace.
0130: */
0131: public static final String MULTIPART_RESOLVER_BEAN_NAME = "portletMultipartResolver";
0132:
0133: /**
0134: * Well-known name for the HandlerMapping object in the bean factory for this namespace.
0135: * Only used when "detectAllHandlerMappings" is turned off.
0136: * @see #setDetectAllViewResolvers
0137: */
0138: public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping";
0139:
0140: /**
0141: * Well-known name for the HandlerAdapter object in the bean factory for this namespace.
0142: * Only used when "detectAllHandlerAdapters" is turned off.
0143: * @see #setDetectAllHandlerAdapters
0144: */
0145: public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter";
0146:
0147: /**
0148: * Well-known name for the HandlerExceptionResolver object in the bean factory for this
0149: * namespace. Only used when "detectAllHandlerExceptionResolvers" is turned off.
0150: * @see #setDetectAllViewResolvers
0151: */
0152: public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver";
0153:
0154: /**
0155: * Well-known name for the ViewResolver object in the bean factory for this namespace.
0156: */
0157: public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";
0158:
0159: /**
0160: * Default URL to ViewRendererServlet. This bridge servlet is used to convert
0161: * portlet render requests to servlet requests in order to leverage the view support
0162: * in the <code>org.springframework.web.view</code> package.
0163: */
0164: public static final String DEFAULT_VIEW_RENDERER_URL = "/WEB-INF/servlet/view";
0165:
0166: /**
0167: * Request attribute to hold the currently chosen HandlerExecutionChain.
0168: * Only used for internal optimizations.
0169: */
0170: public static final String HANDLER_EXECUTION_CHAIN_ATTRIBUTE = DispatcherPortlet.class
0171: .getName()
0172: + ".HANDLER";
0173:
0174: /**
0175: * Unlike the Servlet version of this class, we have to deal with the
0176: * two-phase nature of the portlet request. To do this, we need to pass
0177: * forward any exception that occurs during the action phase, so that
0178: * it can be displayed in the render phase. The only direct way to pass
0179: * things forward and preserve them for each render request is through
0180: * render parameters, but these are limited to String objects and we need
0181: * to pass the Exception itself. The only other way to do this is in the
0182: * session. The bad thing about using the session is that we have no way
0183: * of knowing when we are done re-rendering the request and so we don't
0184: * know when we can remove the objects from the session. So we will end
0185: * up polluting the session with an old exception when we finally leave
0186: * the render phase of one request and move on to something else.
0187: */
0188: public static final String ACTION_EXCEPTION_SESSION_ATTRIBUTE = DispatcherPortlet.class
0189: .getName()
0190: + ".ACTION_EXCEPTION";
0191:
0192: /**
0193: * This render parameter is used to indicate forward to the render phase
0194: * that an exception occurred during the action phase.
0195: */
0196: public static final String ACTION_EXCEPTION_RENDER_PARAMETER = "actionException";
0197:
0198: /**
0199: * Log category to use when no mapped handler is found for a request.
0200: */
0201: public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.portlet.PageNotFound";
0202:
0203: /**
0204: * Name of the class path resource (relative to the DispatcherPortlet class)
0205: * that defines DispatcherPortet's default strategy names.
0206: */
0207: private static final String DEFAULT_STRATEGIES_PATH = "DispatcherPortlet.properties";
0208:
0209: /**
0210: * Additional logger to use when no mapped handler is found for a request.
0211: */
0212: protected static final Log pageNotFoundLogger = LogFactory
0213: .getLog(PAGE_NOT_FOUND_LOG_CATEGORY);
0214:
0215: private static final Properties defaultStrategies;
0216:
0217: static {
0218: // Load default strategy implementations from properties file.
0219: // This is currently strictly internal and not meant to be customized
0220: // by application developers.
0221: try {
0222: ClassPathResource resource = new ClassPathResource(
0223: DEFAULT_STRATEGIES_PATH, DispatcherPortlet.class);
0224: defaultStrategies = PropertiesLoaderUtils
0225: .loadProperties(resource);
0226: } catch (IOException ex) {
0227: throw new IllegalStateException(
0228: "Could not load 'DispatcherPortlet.properties': "
0229: + ex.getMessage());
0230: }
0231: }
0232:
0233: /** Detect all HandlerMappings or just expect "handlerMapping" bean? */
0234: private boolean detectAllHandlerMappings = true;
0235:
0236: /** Detect all HandlerAdapters or just expect "handlerAdapter" bean? */
0237: private boolean detectAllHandlerAdapters = true;
0238:
0239: /** Detect all HandlerExceptionResolvers or just expect "handlerExceptionResolver" bean? */
0240: private boolean detectAllHandlerExceptionResolvers = true;
0241:
0242: /** Detect all ViewResolvers or just expect "viewResolver" bean? */
0243: private boolean detectAllViewResolvers = true;
0244:
0245: /** URL that points to the ViewRendererServlet */
0246: private String viewRendererUrl = DEFAULT_VIEW_RENDERER_URL;
0247:
0248: /** Expose LocaleContext and RequestAttributes as inheritable for child threads? */
0249: private boolean threadContextInheritable = false;
0250:
0251: /** MultipartResolver used by this portlet */
0252: private PortletMultipartResolver multipartResolver;
0253:
0254: /** List of HandlerMappings used by this portlet */
0255: private List handlerMappings;
0256:
0257: /** List of HandlerAdapters used by this portlet */
0258: private List handlerAdapters;
0259:
0260: /** List of HandlerExceptionResolvers used by this portlet */
0261: private List handlerExceptionResolvers;
0262:
0263: /** List of ViewResolvers used by this portlet */
0264: private List viewResolvers;
0265:
0266: /**
0267: * Set whether to detect all HandlerMapping beans in this portlet's context.
0268: * Else, just a single bean with name "handlerMapping" will be expected.
0269: * <p>Default is true. Turn this off if you want this portlet to use a
0270: * single HandlerMapping, despite multiple HandlerMapping beans being
0271: * defined in the context.
0272: */
0273: public void setDetectAllHandlerMappings(
0274: boolean detectAllHandlerMappings) {
0275: this .detectAllHandlerMappings = detectAllHandlerMappings;
0276: }
0277:
0278: /**
0279: * Set whether to detect all HandlerAdapter beans in this portlet's context.
0280: * Else, just a single bean with name "handlerAdapter" will be expected.
0281: * <p>Default is "true". Turn this off if you want this portlet to use a
0282: * single HandlerAdapter, despite multiple HandlerAdapter beans being
0283: * defined in the context.
0284: */
0285: public void setDetectAllHandlerAdapters(
0286: boolean detectAllHandlerAdapters) {
0287: this .detectAllHandlerAdapters = detectAllHandlerAdapters;
0288: }
0289:
0290: /**
0291: * Set whether to detect all HandlerExceptionResolver beans in this portlet's context.
0292: * Else, just a single bean with name "handlerExceptionResolver" will be expected.
0293: * <p>Default is true. Turn this off if you want this portlet to use a
0294: * single HandlerExceptionResolver, despite multiple HandlerExceptionResolver
0295: * beans being defined in the context.
0296: */
0297: public void setDetectAllHandlerExceptionResolvers(
0298: boolean detectAllHandlerExceptionResolvers) {
0299: this .detectAllHandlerExceptionResolvers = detectAllHandlerExceptionResolvers;
0300: }
0301:
0302: /**
0303: * Set whether to detect all ViewResolver beans in this portlet's context.
0304: * Else, just a single bean with name "viewResolver" will be expected.
0305: * <p>Default is true. Turn this off if you want this portlet to use a
0306: * single ViewResolver, despite multiple ViewResolver beans being
0307: * defined in the context.
0308: */
0309: public void setDetectAllViewResolvers(boolean detectAllViewResolvers) {
0310: this .detectAllViewResolvers = detectAllViewResolvers;
0311: }
0312:
0313: /**
0314: * Set the URL to the ViewRendererServlet. That servlet is used to
0315: * ultimately render all views in the portlet application.
0316: */
0317: public void setViewRendererUrl(String viewRendererUrl) {
0318: this .viewRendererUrl = viewRendererUrl;
0319: }
0320:
0321: /**
0322: * Set whether to expose the LocaleContext and RequestAttributes as inheritable
0323: * for child threads (using an {@link java.lang.InheritableThreadLocal}).
0324: * <p>Default is "false", to avoid side effects on spawned background threads.
0325: * Switch this to "true" to enable inheritance for custom child threads which
0326: * are spawned during request processing and only used for this request
0327: * (that is, ending after their initial task, without reuse of the thread).
0328: * <p><b>WARNING:</b> Do not use inheritance for child threads if you are
0329: * accessing a thread pool which is configured to potentially add new threads
0330: * on demand (e.g. a JDK {@link java.util.concurrent.ThreadPoolExecutor}),
0331: * since this will expose the inherited context to such a pooled thread.
0332: */
0333: public void setThreadContextInheritable(
0334: boolean threadContextInheritable) {
0335: this .threadContextInheritable = threadContextInheritable;
0336: }
0337:
0338: /**
0339: * This implementation calls {@link #initStrategies}.
0340: */
0341: public void onRefresh(ApplicationContext context) {
0342: initStrategies(context);
0343: }
0344:
0345: /**
0346: * Refresh the strategy objects that this portlet uses.
0347: * <p>May be overridden in subclasses in order to initialize
0348: * further strategy objects.
0349: */
0350: protected void initStrategies(ApplicationContext context) {
0351: initMultipartResolver(context);
0352: initHandlerMappings(context);
0353: initHandlerAdapters(context);
0354: initHandlerExceptionResolvers(context);
0355: initViewResolvers(context);
0356: }
0357:
0358: /**
0359: * Initialize the PortletMultipartResolver used by this class.
0360: * <p>If no valid bean is defined with the given name in the BeanFactory
0361: * for this namespace, no multipart handling is provided.
0362: */
0363: private void initMultipartResolver(ApplicationContext context) {
0364: try {
0365: this .multipartResolver = (PortletMultipartResolver) context
0366: .getBean(MULTIPART_RESOLVER_BEAN_NAME,
0367: PortletMultipartResolver.class);
0368: if (logger.isDebugEnabled()) {
0369: logger.debug("Using MultipartResolver ["
0370: + this .multipartResolver + "]");
0371: }
0372: } catch (NoSuchBeanDefinitionException ex) {
0373: // Default is no multipart resolver.
0374: this .multipartResolver = null;
0375: if (logger.isDebugEnabled()) {
0376: logger
0377: .debug("Unable to locate PortletMultipartResolver with name '"
0378: + MULTIPART_RESOLVER_BEAN_NAME
0379: + "': no multipart request handling provided");
0380: }
0381: }
0382: }
0383:
0384: /**
0385: * Initialize the HandlerMappings used by this class.
0386: * <p>If no HandlerMapping beans are defined in the BeanFactory
0387: * for this namespace, we default to PortletModeHandlerMapping.
0388: */
0389: private void initHandlerMappings(ApplicationContext context) {
0390: this .handlerMappings = null;
0391:
0392: if (this .detectAllHandlerMappings) {
0393: // Find all HandlerMappings in the ApplicationContext,
0394: // including ancestor contexts.
0395: Map matchingBeans = BeanFactoryUtils
0396: .beansOfTypeIncludingAncestors(context,
0397: HandlerMapping.class, true, false);
0398: if (!matchingBeans.isEmpty()) {
0399: this .handlerMappings = new ArrayList(matchingBeans
0400: .values());
0401: // We keep HandlerMappings in sorted order.
0402: Collections.sort(this .handlerMappings,
0403: new OrderComparator());
0404: }
0405: } else {
0406: try {
0407: Object hm = context.getBean(HANDLER_MAPPING_BEAN_NAME,
0408: HandlerMapping.class);
0409: this .handlerMappings = Collections.singletonList(hm);
0410: } catch (NoSuchBeanDefinitionException ex) {
0411: // Ignore, we'll add a default HandlerMapping later.
0412: }
0413: }
0414:
0415: // Ensure we have at least one HandlerMapping, by registering
0416: // a default HandlerMapping if no other mappings are found.
0417: if (this .handlerMappings == null) {
0418: this .handlerMappings = getDefaultStrategies(context,
0419: HandlerMapping.class);
0420: if (logger.isDebugEnabled()) {
0421: logger.debug("No HandlerMappings found in portlet '"
0422: + getPortletName() + "': using default");
0423: }
0424: }
0425: }
0426:
0427: /**
0428: * Initialize the HandlerAdapters used by this class.
0429: * <p>If no HandlerAdapter beans are defined in the BeanFactory
0430: * for this namespace, we default to SimpleControllerHandlerAdapter.
0431: */
0432: private void initHandlerAdapters(ApplicationContext context) {
0433: this .handlerAdapters = null;
0434:
0435: if (this .detectAllHandlerAdapters) {
0436: // Find all HandlerAdapters in the ApplicationContext,
0437: // including ancestor contexts.
0438: Map matchingBeans = BeanFactoryUtils
0439: .beansOfTypeIncludingAncestors(context,
0440: HandlerAdapter.class, true, false);
0441: if (!matchingBeans.isEmpty()) {
0442: this .handlerAdapters = new ArrayList(matchingBeans
0443: .values());
0444: // We keep HandlerAdapters in sorted order.
0445: Collections.sort(this .handlerAdapters,
0446: new OrderComparator());
0447: }
0448: } else {
0449: try {
0450: Object ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME,
0451: HandlerAdapter.class);
0452: this .handlerAdapters = Collections.singletonList(ha);
0453: } catch (NoSuchBeanDefinitionException ex) {
0454: // Ignore, we'll add a default HandlerAdapter later.
0455: }
0456: }
0457:
0458: // Ensure we have at least some HandlerAdapters, by registering
0459: // default HandlerAdapters if no other adapters are found.
0460: if (this .handlerAdapters == null) {
0461: this .handlerAdapters = getDefaultStrategies(context,
0462: HandlerAdapter.class);
0463: if (logger.isDebugEnabled()) {
0464: logger.debug("No HandlerAdapters found in portlet '"
0465: + getPortletName() + "': using default");
0466: }
0467: }
0468: }
0469:
0470: /**
0471: * Initialize the HandlerExceptionResolver used by this class.
0472: * <p>If no bean is defined with the given name in the BeanFactory
0473: * for this namespace, we default to no exception resolver.
0474: */
0475: private void initHandlerExceptionResolvers(
0476: ApplicationContext context) {
0477: this .handlerExceptionResolvers = null;
0478:
0479: if (this .detectAllHandlerExceptionResolvers) {
0480: // Find all HandlerExceptionResolvers in the ApplicationContext,
0481: // including ancestor contexts.
0482: Map matchingBeans = BeanFactoryUtils
0483: .beansOfTypeIncludingAncestors(context,
0484: HandlerExceptionResolver.class, true, false);
0485: if (!matchingBeans.isEmpty()) {
0486: this .handlerExceptionResolvers = new ArrayList(
0487: matchingBeans.values());
0488: // We keep HandlerExceptionResolvers in sorted order.
0489: Collections.sort(this .handlerExceptionResolvers,
0490: new OrderComparator());
0491: }
0492: } else {
0493: try {
0494: Object her = context.getBean(
0495: HANDLER_EXCEPTION_RESOLVER_BEAN_NAME,
0496: HandlerExceptionResolver.class);
0497: this .handlerExceptionResolvers = Collections
0498: .singletonList(her);
0499: } catch (NoSuchBeanDefinitionException ex) {
0500: // Ignore, no HandlerExceptionResolver is fine too.
0501: }
0502: }
0503:
0504: // Just for consistency, check for default HandlerExceptionResolvers...
0505: // There aren't any in usual scenarios.
0506: if (this .handlerExceptionResolvers == null) {
0507: this .handlerExceptionResolvers = getDefaultStrategies(
0508: context, HandlerExceptionResolver.class);
0509: if (logger.isDebugEnabled()) {
0510: logger
0511: .debug("No HandlerExceptionResolvers found in portlet '"
0512: + getPortletName() + "': using default");
0513: }
0514: }
0515: }
0516:
0517: /**
0518: * Initialize the ViewResolvers used by this class.
0519: * <p>If no ViewResolver beans are defined in the BeanFactory
0520: * for this namespace, we default to InternalResourceViewResolver.
0521: */
0522: private void initViewResolvers(ApplicationContext context) {
0523: this .viewResolvers = null;
0524:
0525: if (this .detectAllViewResolvers) {
0526: // Find all ViewResolvers in the ApplicationContext,
0527: // including ancestor contexts.
0528: Map matchingBeans = BeanFactoryUtils
0529: .beansOfTypeIncludingAncestors(context,
0530: ViewResolver.class, true, false);
0531: if (!matchingBeans.isEmpty()) {
0532: this .viewResolvers = new ArrayList(matchingBeans
0533: .values());
0534: // We keep ViewResolvers in sorted order.
0535: Collections.sort(this .viewResolvers,
0536: new OrderComparator());
0537: }
0538: } else {
0539: try {
0540: Object vr = context.getBean(VIEW_RESOLVER_BEAN_NAME,
0541: ViewResolver.class);
0542: this .viewResolvers = Collections.singletonList(vr);
0543: } catch (NoSuchBeanDefinitionException ex) {
0544: // Ignore, we'll add a default ViewResolver later.
0545: }
0546: }
0547:
0548: // Ensure we have at least one ViewResolver, by registering
0549: // a default ViewResolver if no other resolvers are found.
0550: if (this .viewResolvers == null) {
0551: this .viewResolvers = getDefaultStrategies(context,
0552: ViewResolver.class);
0553: if (logger.isDebugEnabled()) {
0554: logger.debug("No ViewResolvers found in portlet '"
0555: + getPortletName() + "': using default");
0556: }
0557: }
0558: }
0559:
0560: /**
0561: * Return the default strategy object for the given strategy interface.
0562: * <p>The default implementation delegates to {@link #getDefaultStrategies},
0563: * expecting a single object in the list.
0564: * @param context the current Portlet ApplicationContext
0565: * @param strategyInterface the strategy interface
0566: * @return the corresponding strategy object
0567: * @throws BeansException if initialization failed
0568: * @see #getDefaultStrategies
0569: */
0570: protected Object getDefaultStrategy(ApplicationContext context,
0571: Class strategyInterface) throws BeansException {
0572: List strategies = getDefaultStrategies(context,
0573: strategyInterface);
0574: if (strategies.size() != 1) {
0575: throw new BeanInitializationException(
0576: "DispatcherPortlet needs exactly 1 strategy for interface ["
0577: + strategyInterface.getName() + "]");
0578: }
0579: return strategies.get(0);
0580: }
0581:
0582: /**
0583: * Create a List of default strategy objects for the given strategy interface.
0584: * <p>The default implementation uses the "DispatcherPortlet.properties" file
0585: * (in the same package as the DispatcherPortlet class) to determine the class names.
0586: * It instantiates the strategy objects and satisifies ApplicationContextAware
0587: * if necessary.
0588: * @param context the current Portlet ApplicationContext
0589: * @param strategyInterface the strategy interface
0590: * @return the List of corresponding strategy objects
0591: * @throws BeansException if initialization failed
0592: */
0593: protected List getDefaultStrategies(ApplicationContext context,
0594: Class strategyInterface) throws BeansException {
0595: String key = strategyInterface.getName();
0596: List strategies = null;
0597: String value = defaultStrategies.getProperty(key);
0598: if (value != null) {
0599: String[] classNames = StringUtils
0600: .commaDelimitedListToStringArray(value);
0601: strategies = new ArrayList(classNames.length);
0602: for (int i = 0; i < classNames.length; i++) {
0603: String className = classNames[i];
0604: try {
0605: Class clazz = ClassUtils.forName(className,
0606: getClass().getClassLoader());
0607: Object strategy = createDefaultStrategy(context,
0608: clazz);
0609: strategies.add(strategy);
0610: } catch (ClassNotFoundException ex) {
0611: throw new BeanInitializationException(
0612: "Could not find DispatcherPortlet's default strategy class ["
0613: + className + "] for interface ["
0614: + key + "]", ex);
0615: } catch (LinkageError err) {
0616: throw new BeanInitializationException(
0617: "Error loading DispatcherPortlet's default strategy class ["
0618: + className
0619: + "] for interface ["
0620: + key
0621: + "]: problem with class file or dependent class",
0622: err);
0623: }
0624: }
0625: } else {
0626: strategies = Collections.EMPTY_LIST;
0627: }
0628: return strategies;
0629: }
0630:
0631: /**
0632: * Create a default strategy.
0633: * <p>The default implementation uses
0634: * {@link org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean}.
0635: * @param context the current Portlet ApplicationContext
0636: * @param clazz the strategy implementation class to instantiate
0637: * @return the fully configured strategy instance
0638: * @throws BeansException if initialization failed
0639: * @see org.springframework.context.ApplicationContext#getAutowireCapableBeanFactory()
0640: */
0641: protected Object createDefaultStrategy(ApplicationContext context,
0642: Class clazz) throws BeansException {
0643: return context.getAutowireCapableBeanFactory().createBean(
0644: clazz, AutowireCapableBeanFactory.AUTOWIRE_NO, false);
0645: }
0646:
0647: /**
0648: * Obtain this portlet's PortletMultipartResolver, if any.
0649: * @return the PortletMultipartResolver used by this portlet, or <code>null</code>
0650: * if none (indicating that no multipart support is available)
0651: */
0652: public PortletMultipartResolver getMultipartResolver() {
0653: return this .multipartResolver;
0654: }
0655:
0656: /**
0657: * Processes the actual dispatching to the handler for action requests.
0658: * <p>The handler will be obtained by applying the portlet's HandlerMappings in order.
0659: * The HandlerAdapter will be obtained by querying the portlet's installed
0660: * HandlerAdapters to find the first that supports the handler class.
0661: * @param request current portlet action request
0662: * @param response current portlet Action response
0663: * @throws Exception in case of any kind of processing failure
0664: */
0665: protected void doActionService(ActionRequest request,
0666: ActionResponse response) throws Exception {
0667: if (logger.isDebugEnabled()) {
0668: logger.debug("DispatcherPortlet with name '"
0669: + getPortletName() + "' received action request");
0670: }
0671:
0672: // Expose current LocaleResolver and request as LocaleContext.
0673: LocaleContext previousLocaleContext = LocaleContextHolder
0674: .getLocaleContext();
0675: LocaleContextHolder.setLocaleContext(
0676: buildLocaleContext(request),
0677: this .threadContextInheritable);
0678:
0679: // Expose current RequestAttributes to current thread.
0680: RequestAttributes previousRequestAttributes = RequestContextHolder
0681: .getRequestAttributes();
0682: PortletRequestAttributes requestAttributes = new PortletRequestAttributes(
0683: request);
0684: RequestContextHolder.setRequestAttributes(requestAttributes,
0685: this .threadContextInheritable);
0686:
0687: if (logger.isDebugEnabled()) {
0688: logger.debug("Bound action request context to thread: "
0689: + request);
0690: }
0691:
0692: ActionRequest processedRequest = request;
0693: HandlerExecutionChain mappedHandler = null;
0694: int interceptorIndex = -1;
0695:
0696: try {
0697: processedRequest = checkMultipart(request);
0698:
0699: // Determine handler for the current request.
0700: mappedHandler = getHandler(processedRequest, false);
0701: if (mappedHandler == null
0702: || mappedHandler.getHandler() == null) {
0703: noHandlerFound(processedRequest, response);
0704: return;
0705: }
0706:
0707: // Apply preHandle methods of registered interceptors.
0708: if (mappedHandler.getInterceptors() != null) {
0709: for (int i = 0; i < mappedHandler.getInterceptors().length; i++) {
0710: HandlerInterceptor interceptor = mappedHandler
0711: .getInterceptors()[i];
0712: if (!interceptor.preHandleAction(processedRequest,
0713: response, mappedHandler.getHandler())) {
0714: triggerAfterActionCompletion(mappedHandler,
0715: interceptorIndex, processedRequest,
0716: response, null);
0717: return;
0718: }
0719: interceptorIndex = i;
0720: }
0721: }
0722:
0723: // Actually invoke the handler.
0724: HandlerAdapter ha = getHandlerAdapter(mappedHandler
0725: .getHandler());
0726: ha.handleAction(processedRequest, response, mappedHandler
0727: .getHandler());
0728:
0729: // Trigger after-completion for successful outcome.
0730: triggerAfterActionCompletion(mappedHandler,
0731: interceptorIndex, processedRequest, response, null);
0732: }
0733:
0734: catch (Exception ex) {
0735: // Trigger after-completion for thrown exception.
0736: triggerAfterActionCompletion(mappedHandler,
0737: interceptorIndex, processedRequest, response, ex);
0738: // Forward the exception to the render phase to be displayed.
0739: logger
0740: .debug(
0741: "Caught exception during action phase - forwarding to render phase",
0742: ex);
0743: PortletSession session = request.getPortletSession();
0744: session
0745: .setAttribute(ACTION_EXCEPTION_SESSION_ATTRIBUTE,
0746: ex);
0747: response.setRenderParameter(
0748: ACTION_EXCEPTION_RENDER_PARAMETER, ex.toString());
0749: } catch (Error err) {
0750: PortletException ex = new PortletException(
0751: "Error occured during request processing: "
0752: + err.getMessage(), err);
0753: // Trigger after-completion for thrown exception.
0754: triggerAfterActionCompletion(mappedHandler,
0755: interceptorIndex, processedRequest, response, ex);
0756: throw ex;
0757: }
0758:
0759: finally {
0760: // Clean up any resources used by a multipart request.
0761: if (processedRequest instanceof MultipartActionRequest
0762: && processedRequest != request) {
0763: this .multipartResolver
0764: .cleanupMultipart((MultipartActionRequest) processedRequest);
0765: }
0766:
0767: // Reset thread-bound context.
0768: RequestContextHolder.setRequestAttributes(
0769: previousRequestAttributes,
0770: this .threadContextInheritable);
0771: LocaleContextHolder.setLocaleContext(previousLocaleContext,
0772: this .threadContextInheritable);
0773:
0774: // Clear request attributes.
0775: requestAttributes.requestCompleted();
0776: if (logger.isDebugEnabled()) {
0777: logger
0778: .debug("Cleared thread-bound action request context: "
0779: + request);
0780: }
0781: }
0782: }
0783:
0784: /**
0785: * Processes the actual dispatching to the handler for render requests.
0786: * <p>The handler will be obtained by applying the portlet's HandlerMappings in order.
0787: * The HandlerAdapter will be obtained by querying the portlet's installed
0788: * HandlerAdapters to find the first that supports the handler class.
0789: * @param request current portlet render request
0790: * @param response current portlet render response
0791: * @throws Exception in case of any kind of processing failure
0792: */
0793: protected void doRenderService(RenderRequest request,
0794: RenderResponse response) throws Exception {
0795: if (logger.isDebugEnabled()) {
0796: logger.debug("DispatcherPortlet with name '"
0797: + getPortletName() + "' received render request");
0798: }
0799:
0800: // Expose current LocaleResolver and request as LocaleContext.
0801: LocaleContext previousLocaleContext = LocaleContextHolder
0802: .getLocaleContext();
0803: LocaleContextHolder.setLocaleContext(
0804: buildLocaleContext(request),
0805: this .threadContextInheritable);
0806:
0807: // Expose current RequestAttributes to current thread.
0808: RequestAttributes previousRequestAttributes = RequestContextHolder
0809: .getRequestAttributes();
0810: PortletRequestAttributes requestAttributes = new PortletRequestAttributes(
0811: request);
0812: RequestContextHolder.setRequestAttributes(requestAttributes,
0813: this .threadContextInheritable);
0814:
0815: if (logger.isDebugEnabled()) {
0816: logger.debug("Bound render request context to thread: "
0817: + request);
0818: }
0819:
0820: RenderRequest processedRequest = request;
0821: HandlerExecutionChain mappedHandler = null;
0822: int interceptorIndex = -1;
0823:
0824: try {
0825: ModelAndView mv = null;
0826: try {
0827: // Check for forwarded exception from the action phase
0828: PortletSession session = request
0829: .getPortletSession(false);
0830: if (session != null) {
0831: if (request
0832: .getParameter(ACTION_EXCEPTION_RENDER_PARAMETER) != null) {
0833: Exception ex = (Exception) session
0834: .getAttribute(ACTION_EXCEPTION_SESSION_ATTRIBUTE);
0835: if (ex != null) {
0836: logger
0837: .debug("Render phase found exception caught during action phase - rethrowing it");
0838: throw ex;
0839: }
0840: } else {
0841: session
0842: .removeAttribute(ACTION_EXCEPTION_SESSION_ATTRIBUTE);
0843: }
0844: }
0845:
0846: // Determine handler for the current request.
0847: mappedHandler = getHandler(processedRequest, false);
0848: if (mappedHandler == null
0849: || mappedHandler.getHandler() == null) {
0850: noHandlerFound(processedRequest, response);
0851: return;
0852: }
0853:
0854: // Apply preHandle methods of registered interceptors.
0855: if (mappedHandler.getInterceptors() != null) {
0856: for (int i = 0; i < mappedHandler.getInterceptors().length; i++) {
0857: HandlerInterceptor interceptor = mappedHandler
0858: .getInterceptors()[i];
0859: if (!interceptor.preHandleRender(
0860: processedRequest, response,
0861: mappedHandler.getHandler())) {
0862: triggerAfterRenderCompletion(mappedHandler,
0863: interceptorIndex, processedRequest,
0864: response, null);
0865: return;
0866: }
0867: interceptorIndex = i;
0868: }
0869: }
0870:
0871: // Actually invoke the handler.
0872: HandlerAdapter ha = getHandlerAdapter(mappedHandler
0873: .getHandler());
0874: mv = ha.handleRender(processedRequest, response,
0875: mappedHandler.getHandler());
0876:
0877: // Apply postHandle methods of registered interceptors.
0878: if (mappedHandler.getInterceptors() != null) {
0879: for (int i = mappedHandler.getInterceptors().length - 1; i >= 0; i--) {
0880: HandlerInterceptor interceptor = mappedHandler
0881: .getInterceptors()[i];
0882: interceptor.postHandleRender(processedRequest,
0883: response, mappedHandler.getHandler(),
0884: mv);
0885: }
0886: }
0887: } catch (ModelAndViewDefiningException ex) {
0888: logger
0889: .debug(
0890: "ModelAndViewDefiningException encountered",
0891: ex);
0892: mv = ex.getModelAndView();
0893: } catch (Exception ex) {
0894: Object handler = (mappedHandler != null ? mappedHandler
0895: .getHandler() : null);
0896: mv = processHandlerException(request, response,
0897: handler, ex);
0898: }
0899:
0900: // Did the handler return a view to render?
0901: if (mv != null && !mv.isEmpty()) {
0902: render(mv, processedRequest, response);
0903: } else {
0904: if (logger.isDebugEnabled()) {
0905: logger
0906: .debug("Null ModelAndView returned to DispatcherPortlet with name '"
0907: + getPortletName()
0908: + "': assuming HandlerAdapter completed request handling");
0909: }
0910: }
0911:
0912: // Trigger after-completion for successful outcome.
0913: triggerAfterRenderCompletion(mappedHandler,
0914: interceptorIndex, processedRequest, response, null);
0915: }
0916:
0917: catch (Exception ex) {
0918: // Trigger after-completion for thrown exception.
0919: triggerAfterRenderCompletion(mappedHandler,
0920: interceptorIndex, processedRequest, response, ex);
0921: throw ex;
0922: } catch (Error err) {
0923: PortletException ex = new PortletException(
0924: "Error occured during request processing: "
0925: + err.getMessage(), err);
0926: // Trigger after-completion for thrown exception.
0927: triggerAfterRenderCompletion(mappedHandler,
0928: interceptorIndex, processedRequest, response, ex);
0929: throw ex;
0930: }
0931:
0932: finally {
0933: // Reset thread-bound context.
0934: RequestContextHolder.setRequestAttributes(
0935: previousRequestAttributes,
0936: this .threadContextInheritable);
0937: LocaleContextHolder.setLocaleContext(previousLocaleContext,
0938: this .threadContextInheritable);
0939:
0940: // Clear request attributes.
0941: requestAttributes.requestCompleted();
0942: if (logger.isDebugEnabled()) {
0943: logger
0944: .debug("Cleared thread-bound render request context: "
0945: + request);
0946: }
0947: }
0948: }
0949:
0950: /**
0951: * Build a LocaleContext for the given request, exposing the request's
0952: * primary locale as current locale.
0953: * @param request current HTTP request
0954: * @return the corresponding LocaleContext
0955: */
0956: protected LocaleContext buildLocaleContext(PortletRequest request) {
0957: return new SimpleLocaleContext(request.getLocale());
0958: }
0959:
0960: /**
0961: * Convert the request into a multipart request, and make multipart resolver available.
0962: * If no multipart resolver is set, simply use the existing request.
0963: * @param request current HTTP request
0964: * @return the processed request (multipart wrapper if necessary)
0965: */
0966: protected ActionRequest checkMultipart(ActionRequest request)
0967: throws MultipartException {
0968: if (this .multipartResolver != null
0969: && this .multipartResolver.isMultipart(request)) {
0970: if (request instanceof MultipartActionRequest) {
0971: logger
0972: .debug("Request is already a MultipartActionRequest - probably in a forward");
0973: } else {
0974: return this .multipartResolver.resolveMultipart(request);
0975: }
0976: }
0977: // If not returned before: return original request.
0978: return request;
0979: }
0980:
0981: /**
0982: * Return the HandlerExecutionChain for this request.
0983: * Try all handler mappings in order.
0984: * @param request current portlet request
0985: * @param cache whether to cache the HandlerExecutionChain in a request attribute
0986: * @return the HandlerExceutionChain, or null if no handler could be found
0987: */
0988: protected HandlerExecutionChain getHandler(PortletRequest request,
0989: boolean cache) throws Exception {
0990: HandlerExecutionChain handler = (HandlerExecutionChain) request
0991: .getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
0992: if (handler != null) {
0993: if (!cache) {
0994: request
0995: .removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
0996: }
0997: return handler;
0998: }
0999:
1000: Iterator it = this .handlerMappings.iterator();
1001: while (it.hasNext()) {
1002: HandlerMapping hm = (HandlerMapping) it.next();
1003: if (logger.isDebugEnabled()) {
1004: logger.debug("Testing handler map [" + hm
1005: + "] in DispatcherPortlet with name '"
1006: + getPortletName() + "'");
1007: }
1008: handler = hm.getHandler(request);
1009: if (handler != null) {
1010: if (cache) {
1011: request.setAttribute(
1012: HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler);
1013: }
1014: return handler;
1015: }
1016: }
1017: return null;
1018: }
1019:
1020: /**
1021: * No handler found -> throw appropriate exception.
1022: * @param request current portlet request
1023: * @param response current portlet response
1024: */
1025: protected void noHandlerFound(PortletRequest request,
1026: PortletResponse response) throws PortletException {
1027: if (pageNotFoundLogger.isWarnEnabled()) {
1028: pageNotFoundLogger
1029: .warn("No mapping found for current request "
1030: + "in DispatcherPortlet with name '"
1031: + getPortletName()
1032: + "'"
1033: + " mode '"
1034: + request.getPortletMode()
1035: + "'"
1036: + " type '"
1037: + (request instanceof ActionRequest ? "action"
1038: : "render") + "'" + " session '"
1039: + request.getRequestedSessionId() + "'"
1040: + " user '"
1041: + getUsernameForRequest(request) + "'");
1042: }
1043: throw new UnavailableException("No handler found for request");
1044: }
1045:
1046: /**
1047: * Return the HandlerAdapter for this handler object.
1048: * @param handler the handler object to find an adapter for
1049: * @throws PortletException if no HandlerAdapter can be found for the handler.
1050: * This is a fatal error.
1051: */
1052: protected HandlerAdapter getHandlerAdapter(Object handler)
1053: throws PortletException {
1054: Iterator it = this .handlerAdapters.iterator();
1055: while (it.hasNext()) {
1056: HandlerAdapter ha = (HandlerAdapter) it.next();
1057: if (logger.isDebugEnabled()) {
1058: logger.debug("Testing handler adapter [" + ha + "]");
1059: }
1060: if (ha.supports(handler)) {
1061: return ha;
1062: }
1063: }
1064: throw new PortletException(
1065: "No adapter for handler ["
1066: + handler
1067: + "]: Does your handler implement a supported interface like Controller?");
1068: }
1069:
1070: /**
1071: * Determine an error ModelAndView via the registered HandlerExceptionResolvers.
1072: * @param request current portlet request
1073: * @param response current portlet response
1074: * @param handler the executed handler, or null if none chosen at the time of
1075: * the exception (for example, if multipart resolution failed)
1076: * @param ex the exception that got thrown during handler execution
1077: * @return a corresponding ModelAndView to forward to
1078: * @throws Exception if no error ModelAndView found
1079: */
1080: protected ModelAndView processHandlerException(
1081: RenderRequest request, RenderResponse response,
1082: Object handler, Exception ex) throws Exception {
1083:
1084: ModelAndView exMv = null;
1085: for (Iterator it = this .handlerExceptionResolvers.iterator(); exMv == null
1086: && it.hasNext();) {
1087: HandlerExceptionResolver resolver = (HandlerExceptionResolver) it
1088: .next();
1089: exMv = resolver.resolveException(request, response,
1090: handler, ex);
1091: }
1092: if (exMv != null) {
1093: if (logger.isDebugEnabled()) {
1094: logger
1095: .debug("HandlerExceptionResolver returned ModelAndView ["
1096: + exMv + "] for exception");
1097: }
1098: logger
1099: .warn(
1100: "Handler execution resulted in exception - forwarding to resolved error view",
1101: ex);
1102: return exMv;
1103: } else {
1104: throw ex;
1105: }
1106: }
1107:
1108: /**
1109: * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
1110: * Will just invoke afterCompletion for all interceptors whose preHandle
1111: * invocation has successfully completed and returned true.
1112: * @param mappedHandler the mapped HandlerExecutionChain
1113: * @param interceptorIndex index of last interceptor that successfully completed
1114: * @param ex Exception thrown on handler execution, or null if none
1115: * @see HandlerInterceptor#afterRenderCompletion
1116: */
1117: private void triggerAfterActionCompletion(
1118: HandlerExecutionChain mappedHandler, int interceptorIndex,
1119: ActionRequest request, ActionResponse response, Exception ex)
1120: throws Exception {
1121:
1122: // Apply afterCompletion methods of registered interceptors.
1123: if (mappedHandler != null) {
1124: if (mappedHandler.getInterceptors() != null) {
1125: for (int i = interceptorIndex; i >= 0; i--) {
1126: HandlerInterceptor interceptor = mappedHandler
1127: .getInterceptors()[i];
1128: try {
1129: interceptor.afterActionCompletion(request,
1130: response, mappedHandler.getHandler(),
1131: ex);
1132: } catch (Throwable ex2) {
1133: logger
1134: .error(
1135: "HandlerInterceptor.afterCompletion threw exception",
1136: ex2);
1137: }
1138: }
1139: }
1140: }
1141: }
1142:
1143: /**
1144: * Render the given ModelAndView. This is the last stage in handling a request.
1145: * It may involve resolving the view by name.
1146: * @param mv the ModelAndView to render
1147: * @param request current portlet render request
1148: * @param response current portlet render response
1149: * @throws Exception if there's a problem rendering the view
1150: */
1151: protected void render(ModelAndView mv, RenderRequest request,
1152: RenderResponse response) throws Exception {
1153: View view = null;
1154: if (mv.isReference()) {
1155: // We need to resolve the view name.
1156: view = resolveViewName(mv.getViewName(), mv
1157: .getModelInternal(), request);
1158: if (view == null) {
1159: throw new PortletException(
1160: "Could not resolve view with name '"
1161: + mv.getViewName()
1162: + "' in portlet with name '"
1163: + getPortletName() + "'");
1164: }
1165: } else {
1166: // No need to lookup: the ModelAndView object contains the actual View object.
1167: Object viewObject = mv.getView();
1168: if (viewObject == null) {
1169: throw new PortletException("ModelAndView [" + mv
1170: + "] neither contains a view name nor a "
1171: + "View object in portlet with name '"
1172: + getPortletName() + "'");
1173: }
1174: if (!(viewObject instanceof View)) {
1175: throw new PortletException(
1176: "View object ["
1177: + viewObject
1178: + "] is not an instance of [org.springframework.web.servlet.View] - "
1179: + "DispatcherPortlet does not support any other view types");
1180: }
1181: view = (View) viewObject;
1182: }
1183:
1184: if (view == null) {
1185: throw new PortletException(
1186: "Could not resolve view with name '"
1187: + mv.getViewName()
1188: + "' in portlet with name '"
1189: + getPortletName() + "'");
1190: }
1191:
1192: // Set the content type on the response if needed and if possible.
1193: // The Portlet spec requires the content type to be set on the RenderResponse;
1194: // it's not sufficient to let the View set it on the ServletResponse.
1195: if (response.getContentType() != null) {
1196: if (logger.isDebugEnabled()) {
1197: logger
1198: .debug("Portlet response content type already set to ["
1199: + response.getContentType() + "]");
1200: }
1201: } else {
1202: // No Portlet content type specified yet -> use the view-determined type.
1203: String contentType = view.getContentType();
1204: if (contentType != null) {
1205: if (logger.isDebugEnabled()) {
1206: logger
1207: .debug("Setting portlet response content type to view-determined type ["
1208: + contentType + "]");
1209: }
1210: response.setContentType(contentType);
1211: }
1212: }
1213:
1214: // Expose Portlet ApplicationContext to view objects.
1215: request.setAttribute(
1216: ViewRendererServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE,
1217: getPortletApplicationContext());
1218:
1219: // These attributes are required by the ViewRendererServlet.
1220: request.setAttribute(ViewRendererServlet.VIEW_ATTRIBUTE, view);
1221: request.setAttribute(ViewRendererServlet.MODEL_ATTRIBUTE, mv
1222: .getModel());
1223:
1224: // Unclude the content of the view in the render response.
1225: getPortletContext().getRequestDispatcher(this .viewRendererUrl)
1226: .include(request, response);
1227: }
1228:
1229: /**
1230: * Resolve the given view name into a View object (to be rendered).
1231: * <p>Default implementations asks all ViewResolvers of this dispatcher.
1232: * Can be overridden for custom resolution strategies, potentially based
1233: * on specific model attributes or request parameters.
1234: * @param viewName the name of the view to resolve
1235: * @param model the model to be passed to the view
1236: * @param request current portlet render request
1237: * @return the View object, or null if none found
1238: * @throws Exception if the view cannot be resolved
1239: * (typically in case of problems creating an actual View object)
1240: * @see ViewResolver#resolveViewName
1241: */
1242: protected View resolveViewName(String viewName, Map model,
1243: RenderRequest request) throws Exception {
1244: for (Iterator it = this .viewResolvers.iterator(); it.hasNext();) {
1245: ViewResolver viewResolver = (ViewResolver) it.next();
1246: View view = viewResolver.resolveViewName(viewName, request
1247: .getLocale());
1248: if (view != null) {
1249: return view;
1250: }
1251: }
1252: return null;
1253: }
1254:
1255: /**
1256: * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
1257: * Will just invoke afterCompletion for all interceptors whose preHandle
1258: * invocation has successfully completed and returned true.
1259: * @param mappedHandler the mapped HandlerExecutionChain
1260: * @param interceptorIndex index of last interceptor that successfully completed
1261: * @param ex Exception thrown on handler execution, or null if none
1262: * @see HandlerInterceptor#afterRenderCompletion
1263: */
1264: private void triggerAfterRenderCompletion(
1265: HandlerExecutionChain mappedHandler, int interceptorIndex,
1266: RenderRequest request, RenderResponse response, Exception ex)
1267: throws Exception {
1268:
1269: // Apply afterCompletion methods of registered interceptors.
1270: if (mappedHandler != null) {
1271: if (mappedHandler.getInterceptors() != null) {
1272: for (int i = interceptorIndex; i >= 0; i--) {
1273: HandlerInterceptor interceptor = mappedHandler
1274: .getInterceptors()[i];
1275: try {
1276: interceptor.afterRenderCompletion(request,
1277: response, mappedHandler.getHandler(),
1278: ex);
1279: } catch (Throwable ex2) {
1280: logger
1281: .error(
1282: "HandlerInterceptor.afterCompletion threw exception",
1283: ex2);
1284: }
1285: }
1286: }
1287: }
1288: }
1289:
1290: }
|