This commit is contained in:
Keith Donald
2009-03-10 03:30:21 +00:00
parent 53ee66b04e
commit 2b596db2cd
7 changed files with 51 additions and 23 deletions

View File

@@ -2,7 +2,7 @@ SPRING WEB FLOW CHANGELOG
========================= =========================
http://www.springframework.org/webflow http://www.springframework.org/webflow
Changes in version 2.0.6 (2009.02.26) Changes in version 2.0.6 (2009.03.12)
------------------------------------- -------------------------------------
Bug Fixes Bug Fixes
* Fixed bug where FlowHandlerMapping could not handle null flow id scenarios (SWF-968) * Fixed bug where FlowHandlerMapping could not handle null flow id scenarios (SWF-968)
@@ -14,9 +14,10 @@ Bug Fixes
* Fixed bug where form:checkbox and form:checkboxes tags did not apply type conversion properly in views rendered by Web Flows (SWF-986) * Fixed bug where form:checkbox and form:checkboxes tags did not apply type conversion properly in views rendered by Web Flows (SWF-986)
* Fixed bug where Errors nestedPath attribute was not respected in views rendered by Web Flow (SWF-973) * Fixed bug where Errors nestedPath attribute was not respected in views rendered by Web Flow (SWF-973)
* Fixed bug where Errors nestedPath attribute was not respected when using Errors API programatically within a Validator called by Web Flow (SWF-973) * Fixed bug where Errors nestedPath attribute was not respected when using Errors API programatically within a Validator called by Web Flow (SWF-973)
* Made MessageCodesResolver pluggable on MvcViewFactoryCreator; configure a DefaultMessageCodesResolver to resolve error message codes consistent with default Spring MVC behavior (SWF-977).
Improvements Improvements
* Improved FlowExecution test documentation (SWF-???, SWF-???) * Improved FlowExecution test documentation
Changes in version 2.0.5 (14.11.2008) Changes in version 2.0.5 (14.11.2008)
------------------------------------- -------------------------------------

View File

@@ -25,6 +25,8 @@ import org.springframework.binding.expression.beanwrapper.BeanWrapperExpressionP
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.validation.DefaultMessageCodesResolver;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.web.servlet.View; import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.ViewResolver;
import org.springframework.webflow.engine.builder.BinderConfiguration; import org.springframework.webflow.engine.builder.BinderConfiguration;
@@ -34,6 +36,7 @@ import org.springframework.webflow.mvc.portlet.PortletMvcViewFactory;
import org.springframework.webflow.mvc.servlet.ServletMvcViewFactory; import org.springframework.webflow.mvc.servlet.ServletMvcViewFactory;
import org.springframework.webflow.mvc.view.AbstractMvcViewFactory; import org.springframework.webflow.mvc.view.AbstractMvcViewFactory;
import org.springframework.webflow.mvc.view.FlowViewResolver; import org.springframework.webflow.mvc.view.FlowViewResolver;
import org.springframework.webflow.validation.WebFlowMessageCodesResolver;
/** /**
* Returns {@link ViewFactory view factories} that create native Spring MVC-based views. Used by a FlowBuilder to * Returns {@link ViewFactory view factories} that create native Spring MVC-based views. Used by a FlowBuilder to
@@ -66,6 +69,8 @@ public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationCon
private String fieldMarkerPrefix; private String fieldMarkerPrefix;
private MessageCodesResolver messageCodesResolver;
/** /**
* Create a new Spring MVC View Factory Creator. * Create a new Spring MVC View Factory Creator.
* @see #setDefaultViewSuffix(String) * @see #setDefaultViewSuffix(String)
@@ -74,6 +79,7 @@ public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationCon
* @see #setUseSpringBeanBinding(boolean) * @see #setUseSpringBeanBinding(boolean)
* @see #setFlowViewResolver(FlowViewResolver) * @see #setFlowViewResolver(FlowViewResolver)
* @see #setViewResolvers(List) * @see #setViewResolvers(List)
* @see #setMessageCodesResolver(MessageCodesResolver)
*/ */
public MvcViewFactoryCreator() { public MvcViewFactoryCreator() {
@@ -146,6 +152,16 @@ public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationCon
this.flowViewResolver = new DelegatingFlowViewResolver(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 // implementing ApplicationContextAware
public void setApplicationContext(ApplicationContext applicationContext) { public void setApplicationContext(ApplicationContext applicationContext) {
@@ -172,10 +188,10 @@ public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationCon
ConversionService conversionService, BinderConfiguration binderConfiguration) { ConversionService conversionService, BinderConfiguration binderConfiguration) {
if (environment == MvcEnvironment.SERVLET) { if (environment == MvcEnvironment.SERVLET) {
return new ServletMvcViewFactory(viewId, flowViewResolver, expressionParser, conversionService, return new ServletMvcViewFactory(viewId, flowViewResolver, expressionParser, conversionService,
binderConfiguration); binderConfiguration, messageCodesResolver);
} else if (environment == MvcEnvironment.PORTLET) { } else if (environment == MvcEnvironment.PORTLET) {
return new PortletMvcViewFactory(viewId, flowViewResolver, expressionParser, conversionService, return new PortletMvcViewFactory(viewId, flowViewResolver, expressionParser, conversionService,
binderConfiguration); binderConfiguration, messageCodesResolver);
} else { } else {
throw new IllegalStateException("Web MVC Environment " + environment + " not supported "); throw new IllegalStateException("Web MVC Environment " + environment + " not supported ");
} }

View File

@@ -18,6 +18,7 @@ package org.springframework.webflow.mvc.portlet;
import org.springframework.binding.convert.ConversionService; import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.expression.Expression; import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ExpressionParser; import org.springframework.binding.expression.ExpressionParser;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.web.servlet.View; import org.springframework.web.servlet.View;
import org.springframework.webflow.engine.builder.BinderConfiguration; import org.springframework.webflow.engine.builder.BinderConfiguration;
import org.springframework.webflow.execution.RequestContext; import org.springframework.webflow.execution.RequestContext;
@@ -40,10 +41,12 @@ public class PortletMvcViewFactory extends AbstractMvcViewFactory {
* @param expressionParser the expression parser * @param expressionParser the expression parser
* @param conversionService the conversion service * @param conversionService the conversion service
* @param binderConfiguration the model binding configuration * @param binderConfiguration the model binding configuration
* @param messageCodesResolver
*/ */
public PortletMvcViewFactory(Expression viewId, FlowViewResolver viewResolver, ExpressionParser expressionParser, public PortletMvcViewFactory(Expression viewId, FlowViewResolver viewResolver, ExpressionParser expressionParser,
ConversionService conversionService, BinderConfiguration binderConfiguration) { ConversionService conversionService, BinderConfiguration binderConfiguration,
super(viewId, viewResolver, expressionParser, conversionService, binderConfiguration); MessageCodesResolver messageCodesResolver) {
super(viewId, viewResolver, expressionParser, conversionService, binderConfiguration, messageCodesResolver);
} }
protected AbstractMvcView createMvcView(View view, RequestContext context) { protected AbstractMvcView createMvcView(View view, RequestContext context) {

View File

@@ -18,6 +18,7 @@ package org.springframework.webflow.mvc.servlet;
import org.springframework.binding.convert.ConversionService; import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.expression.Expression; import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ExpressionParser; import org.springframework.binding.expression.ExpressionParser;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.web.servlet.View; import org.springframework.web.servlet.View;
import org.springframework.webflow.engine.builder.BinderConfiguration; import org.springframework.webflow.engine.builder.BinderConfiguration;
import org.springframework.webflow.execution.RequestContext; import org.springframework.webflow.execution.RequestContext;
@@ -41,8 +42,9 @@ public class ServletMvcViewFactory extends AbstractMvcViewFactory {
* @param binderConfiguration the model binding configuration * @param binderConfiguration the model binding configuration
*/ */
public ServletMvcViewFactory(Expression viewId, FlowViewResolver viewResolver, ExpressionParser expressionParser, public ServletMvcViewFactory(Expression viewId, FlowViewResolver viewResolver, ExpressionParser expressionParser,
ConversionService conversionService, BinderConfiguration binderConfiguration) { ConversionService conversionService, BinderConfiguration binderConfiguration,
super(viewId, viewResolver, expressionParser, conversionService, binderConfiguration); MessageCodesResolver messageCodesResolver) {
super(viewId, viewResolver, expressionParser, conversionService, binderConfiguration, messageCodesResolver);
} }
protected AbstractMvcView createMvcView(View view, RequestContext context) { protected AbstractMvcView createMvcView(View view, RequestContext context) {

View File

@@ -88,7 +88,7 @@ public abstract class AbstractMvcView implements View {
private BinderConfiguration binderConfiguration; private BinderConfiguration binderConfiguration;
private MessageCodesResolver bindingErrorMessageCodesResolver = new WebFlowMessageCodesResolver(); private MessageCodesResolver messageCodesResolver = new WebFlowMessageCodesResolver();
/** /**
* Creates a new MVC view. * Creates a new MVC view.
@@ -126,10 +126,10 @@ public abstract class AbstractMvcView implements View {
/** /**
* Set the message codes resolver to use to resolve bind and validation failure message codes. * Set the message codes resolver to use to resolve bind and validation failure message codes.
* @param bindingErrorMessageCodesResolver the binding error message code resolver to use * @param messageCodesResolver the binding error message code resolver to use
*/ */
public void setBindingErrorMessageCodesResolver(MessageCodesResolver bindingErrorMessageCodesResolver) { public void setMessageCodesResolver(MessageCodesResolver messageCodesResolver) {
this.bindingErrorMessageCodesResolver = bindingErrorMessageCodesResolver; this.messageCodesResolver = messageCodesResolver;
} }
/** /**
@@ -498,7 +498,7 @@ public abstract class AbstractMvcView implements View {
String model = getModelExpression().getExpressionString(); String model = getModelExpression().getExpressionString();
String field = error.getMapping().getTargetExpression().getExpressionString(); String field = error.getMapping().getTargetExpression().getExpressionString();
Class fieldType = error.getMapping().getTargetExpression().getValueType(getModelObject()); Class fieldType = error.getMapping().getTargetExpression().getValueType(getModelObject());
String[] messageCodes = bindingErrorMessageCodesResolver.resolveMessageCodes(error.getCode(), model, field, String[] messageCodes = messageCodesResolver.resolveMessageCodes(error.getCode(), model, field,
fieldType); fieldType);
return new MessageBuilder().error().source(field).codes(messageCodes).resolvableArg(field).defaultText( return new MessageBuilder().error().source(field).codes(messageCodes).resolvableArg(field).defaultText(
error.getCode() + " on " + field).build(); error.getCode() + " on " + field).build();
@@ -517,7 +517,7 @@ public abstract class AbstractMvcView implements View {
logger.debug("Validating model"); logger.debug("Validating model");
} }
new ValidationHelper(model, requestContext, eventId, getModelExpression().getExpressionString(), new ValidationHelper(model, requestContext, eventId, getModelExpression().getExpressionString(),
expressionParser, bindingErrorMessageCodesResolver, mappingResults).validate(); expressionParser, messageCodesResolver, mappingResults).validate();
} }
private static class PropertyNotFoundError implements MappingResultsCriteria { private static class PropertyNotFoundError implements MappingResultsCriteria {

View File

@@ -19,6 +19,7 @@ import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.expression.Expression; import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ExpressionParser; import org.springframework.binding.expression.ExpressionParser;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.webflow.engine.builder.BinderConfiguration; import org.springframework.webflow.engine.builder.BinderConfiguration;
import org.springframework.webflow.execution.RequestContext; import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.View; import org.springframework.webflow.execution.View;
@@ -45,6 +46,8 @@ public abstract class AbstractMvcViewFactory implements ViewFactory {
private String fieldMarkerPrefix; private String fieldMarkerPrefix;
private MessageCodesResolver messageCodesResolver;
/** /**
* Creates a new MVC view factory. * Creates a new MVC view factory.
* @param viewId the id of the view as an expression * @param viewId the id of the view as an expression
@@ -54,12 +57,14 @@ public abstract class AbstractMvcViewFactory implements ViewFactory {
* @param binderConfiguration the model binding configuration * @param binderConfiguration the model binding configuration
*/ */
public AbstractMvcViewFactory(Expression viewId, FlowViewResolver viewResolver, ExpressionParser expressionParser, public AbstractMvcViewFactory(Expression viewId, FlowViewResolver viewResolver, ExpressionParser expressionParser,
ConversionService conversionService, BinderConfiguration binderConfiguration) { ConversionService conversionService, BinderConfiguration binderConfiguration,
MessageCodesResolver messageCodesResolver) {
this.viewId = viewId; this.viewId = viewId;
this.viewResolver = viewResolver; this.viewResolver = viewResolver;
this.expressionParser = expressionParser; this.expressionParser = expressionParser;
this.conversionService = conversionService; this.conversionService = conversionService;
this.binderConfiguration = binderConfiguration; this.binderConfiguration = binderConfiguration;
this.messageCodesResolver = messageCodesResolver;
} }
public void setEventIdParameterName(String eventIdParameterName) { public void setEventIdParameterName(String eventIdParameterName) {
@@ -77,6 +82,7 @@ public abstract class AbstractMvcViewFactory implements ViewFactory {
mvcView.setExpressionParser(expressionParser); mvcView.setExpressionParser(expressionParser);
mvcView.setConversionService(conversionService); mvcView.setConversionService(conversionService);
mvcView.setBinderConfiguration(binderConfiguration); mvcView.setBinderConfiguration(binderConfiguration);
mvcView.setMessageCodesResolver(messageCodesResolver);
if (StringUtils.hasText(eventIdParameterName)) { if (StringUtils.hasText(eventIdParameterName)) {
mvcView.setEventIdParameterName(eventIdParameterName); mvcView.setEventIdParameterName(eventIdParameterName);
} }

View File

@@ -54,7 +54,7 @@ public class ValidationHelper {
private final ExpressionParser expressionParser; private final ExpressionParser expressionParser;
private final MessageCodesResolver bindingErrorMessageCodesResolver; private final MessageCodesResolver messageCodesResolver;
private final MappingResults mappingResults; private final MappingResults mappingResults;
@@ -76,7 +76,7 @@ public class ValidationHelper {
* @param mappingResults object mapping results * @param mappingResults object mapping results
*/ */
public ValidationHelper(Object model, RequestContext requestContext, String eventId, String modelName, public ValidationHelper(Object model, RequestContext requestContext, String eventId, String modelName,
ExpressionParser expressionParser, MessageCodesResolver bindingErrorMessageCodesResolver, ExpressionParser expressionParser, MessageCodesResolver messageCodesResolver,
MappingResults mappingResults) { MappingResults mappingResults) {
Assert.notNull(model, "The model to validate is required"); Assert.notNull(model, "The model to validate is required");
Assert.notNull(requestContext, "The request context for the validator is required"); Assert.notNull(requestContext, "The request context for the validator is required");
@@ -85,7 +85,7 @@ public class ValidationHelper {
this.eventId = eventId; this.eventId = eventId;
this.modelName = modelName; this.modelName = modelName;
this.expressionParser = expressionParser; this.expressionParser = expressionParser;
this.bindingErrorMessageCodesResolver = bindingErrorMessageCodesResolver; this.messageCodesResolver = messageCodesResolver;
this.mappingResults = mappingResults; this.mappingResults = mappingResults;
} }
@@ -128,7 +128,7 @@ public class ValidationHelper {
validateMethod = ReflectionUtils.findMethod(model.getClass(), methodName, new Class[] { Errors.class }); validateMethod = ReflectionUtils.findMethod(model.getClass(), methodName, new Class[] { Errors.class });
if (validateMethod != null) { if (validateMethod != null) {
MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName, MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName,
model, expressionParser, bindingErrorMessageCodesResolver, mappingResults); model, expressionParser, messageCodesResolver, mappingResults);
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Invoking current state model validation method '" + methodName + "(Errors)'"); logger.debug("Invoking current state model validation method '" + methodName + "(Errors)'");
} }
@@ -157,7 +157,7 @@ public class ValidationHelper {
logger.debug("Invoking default model validation method 'validate(Errors)'"); logger.debug("Invoking default model validation method 'validate(Errors)'");
} }
MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName, MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName,
model, expressionParser, bindingErrorMessageCodesResolver, mappingResults); model, expressionParser, messageCodesResolver, mappingResults);
ReflectionUtils.invokeMethod(validateMethod, model, new Object[] { errors }); ReflectionUtils.invokeMethod(validateMethod, model, new Object[] { errors });
return true; return true;
} }
@@ -205,7 +205,7 @@ public class ValidationHelper {
+ ClassUtils.getShortName(model.getClass()) + ", Errors)'"); + ClassUtils.getShortName(model.getClass()) + ", Errors)'");
} }
MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName, MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName,
model, expressionParser, bindingErrorMessageCodesResolver, mappingResults); model, expressionParser, messageCodesResolver, mappingResults);
ReflectionUtils.invokeMethod(validateMethod, validator, new Object[] { model, errors }); ReflectionUtils.invokeMethod(validateMethod, validator, new Object[] { model, errors });
return true; return true;
} }
@@ -227,7 +227,7 @@ public class ValidationHelper {
logger.debug("Invoking Spring Validator '" + ClassUtils.getShortName(validator.getClass()) + "'"); logger.debug("Invoking Spring Validator '" + ClassUtils.getShortName(validator.getClass()) + "'");
} }
MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName, MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName,
model, expressionParser, bindingErrorMessageCodesResolver, mappingResults); model, expressionParser, messageCodesResolver, mappingResults);
((Validator) validator).validate(model, errors); ((Validator) validator).validate(model, errors);
return true; return true;
} }
@@ -252,7 +252,7 @@ public class ValidationHelper {
+ ".validate(" + ClassUtils.getShortName(model.getClass()) + ", Errors)'"); + ".validate(" + ClassUtils.getShortName(model.getClass()) + ", Errors)'");
} }
MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName, MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName,
model, expressionParser, bindingErrorMessageCodesResolver, mappingResults); model, expressionParser, messageCodesResolver, mappingResults);
ReflectionUtils.invokeMethod(validateMethod, validator, new Object[] { model, errors }); ReflectionUtils.invokeMethod(validateMethod, validator, new Object[] { model, errors });
return true; return true;
} }