/* * Copyright 2004-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.webflow.mvc.builder; import java.util.List; import org.springframework.beans.BeanWrapper; import org.springframework.binding.convert.ConversionService; import org.springframework.binding.expression.Expression; import org.springframework.binding.expression.ExpressionParser; import org.springframework.binding.expression.beanwrapper.BeanWrapperExpressionParser; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.util.StringUtils; import org.springframework.validation.DefaultMessageCodesResolver; import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.Validator; import org.springframework.web.servlet.View; import org.springframework.web.servlet.ViewResolver; import org.springframework.webflow.engine.builder.BinderConfiguration; import org.springframework.webflow.engine.builder.ViewFactoryCreator; import org.springframework.webflow.execution.ViewFactory; import org.springframework.webflow.mvc.portlet.PortletMvcViewFactory; import org.springframework.webflow.mvc.servlet.ServletMvcViewFactory; import org.springframework.webflow.mvc.view.AbstractMvcViewFactory; import org.springframework.webflow.mvc.view.FlowViewResolver; import org.springframework.webflow.validation.ValidationHintResolver; import org.springframework.webflow.validation.WebFlowMessageCodesResolver; /** * Returns {@link ViewFactory view factories} that create native Spring MVC-based views. Used by a FlowBuilder to * configure a flow's view states with Spring MVC-based view factories. * <p> * This implementation detects whether it is running in a Servlet or Portlet MVC environment, and returns instances of * the default view factory implementation for that environment. * <p> * By default, this implementation creates view factories that resolve their views by loading flow-relative resources, * such as .jsp templates located in a flow working directory. This class also supports rendering views resolved by * pre-existing Spring MVC {@link ViewResolver view resolvers}. * * @see ServletMvcViewFactory * @see PortletMvcViewFactory * @see FlowResourceFlowViewResolver * @see DelegatingFlowViewResolver * * @author Keith Donald * @author Scott Andrews */ public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationContextAware { private MvcEnvironment environment; private FlowViewResolver flowViewResolver = new FlowResourceFlowViewResolver(); private boolean useSpringBeanBinding; private String eventIdParameterName; private String fieldMarkerPrefix; private MessageCodesResolver messageCodesResolver = new WebFlowMessageCodesResolver(); /** * Create a new Spring MVC View Factory Creator. * @see #setDefaultViewSuffix(String) * @see #setEventIdParameterName(String) * @see #setFieldMarkerPrefix(String) * @see #setUseSpringBeanBinding(boolean) * @see #setFlowViewResolver(FlowViewResolver) * @see #setViewResolvers(List) * @see #setMessageCodesResolver(MessageCodesResolver) */ public MvcViewFactoryCreator() { } /** * Configure an {@link FlowResourceFlowViewResolver} capable of resolving view resources by applying the specified * default resource suffix. Default is .jsp. * @param defaultViewSuffix the default view suffix */ public void setDefaultViewSuffix(String defaultViewSuffix) { FlowResourceFlowViewResolver internalResourceResolver = new FlowResourceFlowViewResolver(); internalResourceResolver.setDefaultViewSuffix(defaultViewSuffix); this.flowViewResolver = internalResourceResolver; } /** * Sets the name of the request parameter to use to lookup user events signaled by views created in this factory. If * not specified, the default is <code>_eventId</code> * @param eventIdParameterName the event id parameter name */ public void setEventIdParameterName(String eventIdParameterName) { this.eventIdParameterName = eventIdParameterName; } /** * Specify a prefix that can be used for parameters that mark potentially empty fields, having "prefix + field" as * name. Such a marker parameter is checked by existence: You can send any value for it, for example "visible". This * is particularly useful for HTML checkboxes and select options. * <p> * Default is "_", for "_FIELD" parameters (e.g. "_subscribeToNewsletter"). Set this to null if you want to turn off * the empty field check completely. * <p> * HTML checkboxes only send a value when they're checked, so it is not possible to detect that a formerly checked * box has just been unchecked, at least not with standard HTML means. * <p> * This auto-reset mechanism addresses this deficiency, provided that a marker parameter is sent for each checkbox * field, like "_subscribeToNewsletter" for a "subscribeToNewsletter" field. As the marker parameter is sent in any * case, the data binder can detect an empty field and automatically reset its value. */ public void setFieldMarkerPrefix(String fieldMarkerPrefix) { this.fieldMarkerPrefix = fieldMarkerPrefix; } /** * Sets whether to use data binding with Spring's {@link BeanWrapper} should be enabled. Set to 'true' to enable. * 'false', disabled, is the default. With this enabled, the same binding system used by Spring MVC 2.x is also used * in a Web Flow environment. * @param useSpringBeanBinding the Spring bean binding flag */ public void setUseSpringBeanBinding(boolean useSpringBeanBinding) { this.useSpringBeanBinding = useSpringBeanBinding; } /** * Set to fully customize how the flow system resolves Spring MVC {@link View} objects. * @param flowViewResolver the flow view resolver */ public void setFlowViewResolver(FlowViewResolver flowViewResolver) { this.flowViewResolver = flowViewResolver; } /** * Sets the chain of Spring MVC {@link ViewResolver view resolvers} to delegate to resolve views selected by flows. * Allows for reuse of existing View Resolvers configured in a Spring application context. If multiple resolvers are * to be used, the resolvers should be ordered in the manner they should be applied. * @param viewResolvers the view resolver list */ public void setViewResolvers(List<ViewResolver> viewResolvers) { this.flowViewResolver = new DelegatingFlowViewResolver(viewResolvers); } /** * Sets the message codes resolver strategy to use to resolve bind and validation error message codes. If not set, * {@link WebFlowMessageCodesResolver} is the default. Plug in a {@link DefaultMessageCodesResolver} to resolve * message codes consistently between Spring MVC Controllers and Web Flow. * @param messageCodesResolver the message codes resolver */ public void setMessageCodesResolver(MessageCodesResolver messageCodesResolver) { this.messageCodesResolver = messageCodesResolver; } // implementing ApplicationContextAware public void setApplicationContext(ApplicationContext applicationContext) { environment = MvcEnvironment.environmentFor(applicationContext); } public ViewFactory createViewFactory(Expression viewId, ExpressionParser expressionParser, ConversionService conversionService, BinderConfiguration binderConfiguration, Validator validator, ValidationHintResolver validationHintResolver) { if (useSpringBeanBinding) { expressionParser = new BeanWrapperExpressionParser(conversionService); } AbstractMvcViewFactory viewFactory = createMvcViewFactory(viewId, expressionParser, conversionService, binderConfiguration); if (StringUtils.hasText(eventIdParameterName)) { viewFactory.setEventIdParameterName(eventIdParameterName); } if (StringUtils.hasText(fieldMarkerPrefix)) { viewFactory.setFieldMarkerPrefix(fieldMarkerPrefix); } viewFactory.setValidator(validator); viewFactory.setValidationHintResolver(validationHintResolver); return viewFactory; } /** * Creates a concrete instance of an AbstractMvcViewFactory according to the runtime environment (Servlet or * Portlet). */ protected AbstractMvcViewFactory createMvcViewFactory(Expression viewId, ExpressionParser expressionParser, ConversionService conversionService, BinderConfiguration binderConfiguration) { if (environment == MvcEnvironment.SERVLET) { return new ServletMvcViewFactory(viewId, flowViewResolver, expressionParser, conversionService, binderConfiguration, messageCodesResolver); } else if (environment == MvcEnvironment.PORTLET) { return new PortletMvcViewFactory(viewId, flowViewResolver, expressionParser, conversionService, binderConfiguration, messageCodesResolver); } else { throw new IllegalStateException("Web MVC Environment " + environment + " not supported "); } } public String getViewIdByConvention(String viewStateId) { return flowViewResolver.getViewIdByConvention(viewStateId); } }