Announcement Announcement Module
Collapse
No announcement yet.
Do binding errors prevent validation? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Do binding errors prevent validation?

    I have previously implemented Spring Portlet MVC applications and have hit a snag trying to implement a validator under SWF 2.0.3

    My validator is a fairly simple affair:

    Code:
    public class ProjectFormValidator {
    
    	public void validateActivityDetails(ProjectForm form, Errors errors) {
    		rejectIfEmptyOrWhitespace(errors, "projectDetail.projectName", form.getProjectDetail().getProjectName(), RdtcConstants.REQUIRED_FIELD_KEY);
    	}
    	
    	private void rejectIfEmptyOrWhitespace(Errors errors, String field, Object fieldValue, String requiredKey){
    		//Object value = errors.getFieldValue(path);
    		if (fieldValue == null ||!StringUtils.hasText(fieldValue.toString())) {
    			errors.rejectValue(field, requiredKey, null, null);
    		}
    	}
    	
    }
    I'm using an Errors instance in the method as opposed to a MessageContext instance as I have a bunch of legacy validation routines that depend on Errors. Based on previous portlet MVC experience, I'd expect my validator to be invoked after binding is complete, with any binding errors pre-populated into the Errors object and with the ability to call errors.getFieldValue() (which should return either the currently bound field value or the user-entered value if a binding error occurred).

    After configuring my validator in the spring and webflow configuration files I'm experiencing two separate but potentially related problems:

    1. My validator is ONLY invoked if no data binding errors occurred. This causes usability issues in our application, as a user will initially be presented with a set of binding-related errors, and after correcting these and re-submitting will potentially be presented with a new, unrelated set of errors (from the validator) that we could/should have been able to tell them about the first time.

    2. Attempting to call errors.getFieldValue() throws an UnsupportedOperationException. I believe this has been previously raised as SWF-823 and looks like it will be fixed in the 2.0.4 release, but it is unclear to me whether the fix just prevents the exception being thrown or whether it will return the user-entered input in the case of a binding failure as specified in the Errors javadoc.

    So are my expectations incorrect, am I missing something else or is there a problem in the framework?

  • #2
    Binding and validation functionality previousily available SWF been lost in SWF 2.0.3

    In using SWF 2.0.3 we have lost some functionality during binding and validation of forms that are submitted to the portal server. This functionality is available in previous version SWF and in the Spring Portlet MVC.

    We have discovered that binding and validation model in SWF 2.0.3 has changed from that used in the previous SWF 1.0 and the standard Spring Portal MVC.

    We can now no longer provide the following functionality

    1. Binding and validation messages in one list, because the checks are no longer done together.

    This means that there are seperate error lists for binding errors and for validation errors.
    With the binding error list being returned first and then the validation checks being perform once all the binding errors have been resolved.

    For the user this means that they will possibly get two successive lists of errors on the html form that must be corrected before they can successfully submit the form to the Portal. In the previous version of the SWF and also the standard Spring Porlet MVC combined the error list from binding and validation together and returned it to the user as one list to be resolved.

    2. In the case where there are binding errors. The fields that have binding errors will be highlighted but are not populated with the data that caused the binding error. Instead the value in the fields will be reverted back to what ever the pervious value they were originally bound to.

    For the end user this means that they will not see the value that they supplied that caused the binding error. And may even be potentially confused by seeing valid data highlighted as an being in error.

    The previous version of SWF and the standard Spring portlet MVC both populate the HTML form field with the data that caused the binding error.

    Comment


    • #3
      Indeed, the Web Flow 2 model validation functionality will not invoke the configured Validator if binding also fails. I have indeed noticed this is inconsistent with FormAction's original "bindAndValidate" method, which always performs validation after binding, even if there are validation errors.

      The con I see with performing validation after binding when there are binding errors is the potential for duplicate error messages. For example, if a type conversion occurs on a new form against a required field, you'll end up with a type conversion error message and a required error message. However, I think the pro's [preserved behavior + more intuitive usability] outweigh this con.

      Thoughts?

      Keith

      Comment


      • #4
        Thanks for your response Keith, we thought this issue was a goner and have coded around it.

        We agree
        [preserved behavior + more intuitive usability] outweigh this con.

        Regarding the duplicate validation problem you describe:
        For example, if a type conversion occurs on a new form against a required field, you'll end up with a type conversion error message and a required error message.
        In spring MVC you would use the errors.getFieldValue() method which returns the bound value or the user entered value if binding failed. Validating against this value would prevent a required error being raised when a binding error occurs.

        For us, the issue described in number 2, above is our biggest problem. In that the value supplied by the user that is causing the binding error is not being bound back to web form when we display our error message. Instead the last valid value bound is put in its place. WE assume this stems from the same problem in that framework cannot use the errors.getFieldValue() method to get the user entered value if binding failed.

        This manifests its self as following usability issue in our application.
        e.g. The user is presented with an html form that takes a pre populated dollar value:


        Research: how much did you plan to spend ? $[ 5000]

        the user enters 7,500 (note: the comma will cause a binding error)

        e.g.
        Research: how much did you plan to spend ? $[ 7,500]



        In this case our user will see a html form with the error message as follows.
        e.g.

        Error - You cannot continue because there is a problem with the information submitted. Please read the message(s) below, make the necessary changes and then continue.

        Research: Please enter a valid dollar amount in whole dollars only (without cents)


        Research: how much did you plan to spend ? $[ 5000]


        Note the existing valid value is bound to the html form rather than the error value 7,500 with a comma that causes the binding error.

        Comment


        • #5
          Thanks for the useful information / context. I am not seeing the #2 issue as you describe it. Erred values that resulting in binding failures should definitely be rendered when the form re-renders so the user can revise their edits. I confirmed this behavior in our reference application. For example, disable JavaScript and try typing in "bogus" for a booking Check In date... when the form re-renders with validation errors, "bogus" is re-rendered instead of the previous valid-value. Is it possible we are thinking of a binding failure as different things? A binding failure occurs when attempting to copy the user-entered value to the target bean, and that copy process fails, leaving the target bean property unmodified. The original user entered value is then tracked in what we call a MappingResult object, and that original value is then exported to the view by our MVC adapter BindingModel [an Errors implementation]. You can see that code here:
          Code:
          public Object getFieldValue(String field) {
          	if (mappingResults != null) {
          		List results = mappingResults.getResults(new FieldErrorResult(field));
          		if (!results.isEmpty()) {
          			MappingResult fieldError = (MappingResult) results.get(0);
          			return fieldError.getResult().getOriginalValue();
          		}
          	}
          	return getFormattedValue(parseFieldExpression(field));
          }
          Would you mind putting a breakponit in this method of BindingModel and see what is going on for your amount field in the form that is having this problem? Is this problem always happening or just sometimes? Have you tried upgrading to Web Flow 2.0.4? As Richard mentioned in Web Flow 2 you can now use the Errors.getFieldValue(String) method to access the original value from your Validator. Thanks Keith
          Last edited by Keith Donald; Nov 11th, 2008, 10:09 AM.

          Comment


          • #6
            Hi Keith,

            in my opinion it would be the best way if it's configurable if validation should be executed after binding errors occurs. So the developer can decide which way to choose. In our projects we have often the case that customers wanna see binding errors and valdation error togther.

            Regards,

            Flashrider

            Comment


            • #7
              Hi Keith,

              Is it possible we are thinking of a binding failure as different things?
              No I think we are on the same page regarding the binding errors. And your test gives the behaviour that we expect. It is good that that the reference implementation is working okay.

              However one thing I think we failed to mention is that we are having this problem in a portal server environment.
              We have tried running with the break point set and the mappingResults == null therefore we don't drop into the code that fetches the get the original value
              Code:
              if (mappingResults != null) {
              		List results = mappingResults.getResults(new FieldErrorResult(field));
              		if (!results.isEmpty()) {
              			MappingResult fieldError = (MappingResult) results.get(0);
              			return fieldError.getResult().getOriginalValue();
              		}
              	}
              Could they MappingResults be getting lost between the action request and render request in the portal environment ?

              Also we have tried deploying the reference application into the pluto portal server and we get the following error when we try the bogus example

              Spring Webflow Booking MVC
              Code:
              Error rendering portlet.
              javax.portlet.PortletException: Request processing failed
              	at org.springframework.web.portlet.FrameworkPortlet.processRequest(FrameworkPortlet.java:496)
              	at org.springframework.web.portlet.FrameworkPortlet.doDispatch(FrameworkPortlet.java:453)
              	at javax.portlet.GenericPortlet.render(GenericPortlet.java:175)
              	at org.apache.pluto.core.PortletServlet.dispatch(PortletServlet.java:208)
              	at org.apache.pluto.core.PortletServlet.doGet(PortletServlet.java:139)
              	at javax.servlet.http.HttpServlet.service(HttpServlet.java:689)
              	at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
              	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
              	at 	
              [snip]
              Caused by: java.text.ParseException: Unparseable date: "bogus"
              	at java.text.DateFormat.parse(DateFormat.java:335)
              	at org.springframework.binding.convert.converters.StringToDate.toObject(StringToDate.java:90)
              	... more
              
              Nested Exception is org.springframework.webflow.execution.FlowExecutionException: Exception thrown in state 'enterBookingDetails' of flow 'booking'
              	at org.springframework.webflow.engine.impl.FlowExecutionImpl.wrap(FlowExecutionImpl.java:568)
              	at org.springframework.webflow.engine.impl.FlowExecutionImpl.resume(FlowExecutionImpl.java:267)
              	at org.springframework.webflow.executor.FlowExecutorImpl.resumeExecution(FlowExecutorImpl.java:153)
              	at org.springframework.webflow.mvc.portlet.FlowHandlerAdapter.handleAction(FlowHandlerAdapter.java:141)
              	at org.springframework.web.portlet.DispatcherPortlet.doActionService(DispatcherPortlet.java:694)
              	at org.springframework.web.portlet.FrameworkPortlet.processRequest(FrameworkPortlet.java:480)
              	at org.springframework.web.portlet.FrameworkPortlet.processAction(FrameworkPortlet.java:462)
              	at org.apache.pluto.core.PortletServlet.dispatch(PortletServlet.java:218)
              	at org.apache.pluto.core.PortletServlet.doPost(PortletServlet.java:145)
              	at javax.servlet.http.HttpServlet.service(HttpServlet.java:709)
              	at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
              	at 
              [snip]
              Caused by: org.springframework.binding.convert.ConversionExecutionException: Unable to convert value bogus from type 'java.lang.String' to type 'java.util.Date; reason = 'Invalid format for value 'bogus'; the expected format was 'yyyy-MM-dd''
              	at org.springframework.binding.convert.service.StaticConversionExecutor.execute(StaticConversionExecutor.java:101)
              	at org.springframework.binding.expression.el.ELExpression.setValue(ELExpression.java:92)
              	at org.springframework.binding.mapping.impl.DefaultMapping.map(DefaultMapping.java:133)
              	at org.springframework.binding.mapping.impl.DefaultMapper.map(DefaultMapper.java:68)
              	at org.springframework.webflow.mvc.view.AbstractMvcView.bind(AbstractMvcView.java:296)
              	at org.springframework.webflow.mvc.view.AbstractMvcView.processUserEvent(AbstractMvcView.java:190)
              	at org.springframework.webflow.engine.ViewState.resume(ViewState.java:199)
              	at org.springframework.webflow.engine.Flow.resume(Flow.java:551)
              	at org.springframework.webflow.engine.impl.FlowExecutionImpl.resume(FlowExecutionImpl.java:263)
              	... 37 more
              Caused by: org.springframework.binding.convert.converters.InvalidFormatException: Invalid format for value 'bogus'; the expected format was 'yyyy-MM-dd'
              	at org.springframework.binding.convert.converters.StringToDate.toObject(StringToDate.java:92)
              	at org.springframework.binding.convert.converters.StringToObject.convertSourceToTargetClass(StringToObject.java:37)
              	at org.springframework.binding.convert.service.StaticConversionExecutor.execute(StaticConversionExecutor.java:99)
              	... 45 more
              Caused by: java.text.ParseException: Unparseable date: "bogus"
              	at java.text.DateFormat.parse(DateFormat.java:335)
              	at org.springframework.binding.convert.converters.StringToDate.toObject(StringToDate.java:90)
              	... 47 more

              Comment


              • #8
                Interesting hypothesis. We'll look into this in a portal environment.

                Regarding your Booking MVC test, was that exception stack thrown out on Web Flow 2.0.4? There were some important fixes in 2.0.4 in this area, and wouldn't expect that to propagate there...

                Keith

                Comment


                • #9
                  Originally posted by Keith Donald View Post
                  Regarding your Booking MVC test, was that exception stack thrown out on Web Flow 2.0.4? There were some important fixes in 2.0.4 in this area, and wouldn't expect that to propagate there...
                  Keith
                  Okay we tried again with 2.0.4 and no longer get the stack. Instead we see the behaviour we have described, the bogus value is lost and the old date value is displayed after the error message.

                  see attached screen shot.

                  Comment


                  • #10
                    What do you think about my suggestion?

                    Comment


                    • #11
                      I think its a good suggestion. How would you suggest it be implemented? We already have bind, validate attributes on transition (that can be marked true/false). So where do you think we should go from here, keeping in mind compatibility?

                      Keith

                      Comment


                      • #12
                        Hi,

                        I would not introduce an new transition attribute fo that. It it a global configuration that should just be definded once in an application. But I can not tell you the right place where to do that.

                        Regards,

                        Flashrider

                        Comment


                        • #13
                          Well, there are several places. You could configure this per flow definition (flow definition attribute), across executions of all flows (flow execution attribute), or per view-state transition. Why do you think its best as a global setting, and not per transition?

                          Keith

                          Comment


                          • #14
                            I think the best solution is that it can be configured in serveal places. A global configuration maybe as flow execution attribute and local one in a transition.

                            In my case, if I want that the validation is executed after binding errors I want that for the whole project. It's not nice if i have to write the attribute in each transition.

                            Comment


                            • #15
                              We've created corresponding JIRA issues for these problems, scheduled for 2.0.5

                              You can track these issues here: http://jira.springframework.org/secu...rter/order=ASC

                              It'd be great if you could test out 2.0.5 nightly builds as these issues are fixed so we know we've got them right before 2.0.5 goes out.

                              Keith

                              Comment

                              Working...
                              X