I have a Roo-generated Spring MVC app to which I'm adding web flow. I'm using:
- SWF 2.1.1.RELEASE
- Spring 3.0.4.RELEASE
- Hibernate Validator 4.0.2.GA (JSR-303 provider)
- Roo 1.0.2
Domain Model
I have a simple entity called Book, which has a title and a publisher:
Code:
@Entity
@RooJavaBean
@RooToString
@RooEntity
@BookConstraint(message = "Oops!")
public class Book implements Serializable {
@Column(length = 30)
@NotNull
@Size(min = 1, max = 30)
private String title;
@ManyToOne(targetEntity = Publisher.class)
@NotNull
@JoinColumn
private Publisher publisher;
}
Validation
Apart from the above field-level JSR-303 annotations, I also have a custom ConstraintValidator with its corresponding annotation, as follows:
Code:
public class BookConstraintValidator
implements ConstraintValidator<BookConstraint, Book>
{
@Override
public void initialize(BookConstraint constraintAnnotation) {
// Empty
}
@Override
public boolean isValid(Book book, ConstraintValidatorContext context) {
return false; // i.e. validation is hard-coded to fail for now
}
}
Code:
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = BookConstraintValidator.class)
public @interface BookConstraint {
String message() default "{some.error.code}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Works with MVC
This all works nicely when adding a Book via straight Spring MVC (i.e. outside of SWF). The validation fails as expected, and the view displays the configured "Oops!" error message.
Integration of JSR-303 with SWF
Rather than duplicate all the above validation rules in SWF-style validators, I have set up SWF to delegate to my JSR-303 validation logic via a LocalValidatorFactoryBean. However when I add a new Book via SWF, I get this exception:
Code:
org.springframework.webflow.execution.FlowExecutionException: Exception thrown in state 'enterBookDetails' of flow 'flows/newBook'
at org.springframework.webflow.engine.impl.FlowExecutionImpl.wrap(FlowExecutionImpl.java:569)
at org.springframework.webflow.engine.impl.FlowExecutionImpl.resume(FlowExecutionImpl.java:263)
at org.springframework.webflow.executor.FlowExecutorImpl.resumeExecution(FlowExecutorImpl.java:169)
at org.springframework.webflow.mvc.servlet.FlowHandlerAdapter.handle(FlowHandlerAdapter.java:183)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:788)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:717)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
...
Caused by: java.lang.IllegalArgumentException: The expression string to parse is required and must not be empty
at org.springframework.util.Assert.hasText(Assert.java:162)
at org.springframework.binding.expression.spel.SpringELExpressionParser.parseExpression(SpringELExpressionParser.java:74)
at org.springframework.binding.message.MessageContextErrors.rejectValue(MessageContextErrors.java:86)
at org.springframework.validation.beanvalidation.SpringValidatorAdapter.validate(SpringValidatorAdapter.java:92)
at com.example.books.domain.validate.BookValidator.validate(BookValidator.java:17)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:186)
at org.springframework.webflow.validation.ValidationHelper.invokeValidatorDefaultValidateMethod(ValidationHelper.java:256)
at org.springframework.webflow.validation.ValidationHelper.invokeModelValidator(ValidationHelper.java:185)
at org.springframework.webflow.validation.ValidationHelper.validate(ValidationHelper.java:104)
at org.springframework.webflow.mvc.view.AbstractMvcView.validate(AbstractMvcView.java:625)
at org.springframework.webflow.mvc.view.AbstractMvcView.processUserEvent(AbstractMvcView.java:217)
at org.springframework.webflow.engine.ViewState.handleEvent(ViewState.java:224)
at org.springframework.webflow.engine.ViewState.resume(ViewState.java:196)
at org.springframework.webflow.engine.Flow.resume(Flow.java:545)
at org.springframework.webflow.engine.impl.FlowExecutionImpl.resume(FlowExecutionImpl.java:259)
... 47 more
java.lang.IllegalArgumentException
The expression string to parse is required and must not be empty
java.lang.IllegalArgumentException: The expression string to parse is required and must not be empty
at org.springframework.util.Assert.hasText(Assert.java:162)
at org.springframework.binding.expression.spel.SpringELExpressionParser.parseExpression(SpringELExpressionParser.java:74)
at org.springframework.binding.message.MessageContextErrors.rejectValue(MessageContextErrors.java:86)
at org.springframework.validation.beanvalidation.SpringValidatorAdapter.validate(SpringValidatorAdapter.java:92)
at com.example.books.domain.validate.BookValidator.validate(BookValidator.java:17)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:186)
at org.springframework.webflow.validation.ValidationHelper.invokeValidatorDefaultValidateMethod(ValidationHelper.java:256)
at org.springframework.webflow.validation.ValidationHelper.invokeModelValidator(ValidationHelper.java:185)
at org.springframework.webflow.validation.ValidationHelper.validate(ValidationHelper.java:104)
at org.springframework.webflow.mvc.view.AbstractMvcView.validate(AbstractMvcView.java:625)
at org.springframework.webflow.mvc.view.AbstractMvcView.processUserEvent(AbstractMvcView.java:217)
at org.springframework.webflow.engine.ViewState.handleEvent(ViewState.java:224)
at org.springframework.webflow.engine.ViewState.resume(ViewState.java:196)
at org.springframework.webflow.engine.Flow.resume(Flow.java:545)
at org.springframework.webflow.engine.impl.FlowExecutionImpl.resume(FlowExecutionImpl.java:259)
at org.springframework.webflow.executor.FlowExecutorImpl.resumeExecution(FlowExecutorImpl.java:169)
at org.springframework.webflow.mvc.servlet.FlowHandlerAdapter.handle(FlowHandlerAdapter.java:183)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:788)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:717)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:362)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:726)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:285)
at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:126)
...
I'm looking into this as we speak, but I'm wondering if anyone else has come across a similar problem, or has managed to get a ConstraintValidator working with SWF.
Thanks in advance for any help.