Saturday 13 August 2011

Some corner cases of cross field validation with the ExtVal framework

Introduction

ExtVal has the ability to perform some cross field validations. An example is the check if two fields have the same content or to verify the relation between two dates (is one date 'before' the other). Besides the standard field validation and the object validation, it is the third way of performing validations. The advantage of the cross field validation is that the update model phase isn't required to have all the values to perform the validation. Due to the ProcessedInformationRecorder concept, the framework can record all values entered in the screen during the model validation phase. And then in a PhaseListener, these values can be used to perform the cross field validation.
This text explains what happens when you use some more exotic expressions.

No managed bean

We all use a managed bean and his properties to store the values of the inputText tags as follows

<h:inputText value="#{bean.source}" label="source" id="source"/>
 
But this isn't the only way how we can define the associated property for an input tag. Suppose you define in the faces-config file a String value under a certain name, then you can use it as attribute value. Is it useful? Certainly, imagine the case that you ask for a password and the retyped password to be certain the user hasn't made a type error. But we aren't interested in the retyped password value; we only need to know if it is equals to the other typed value.
So we can created something like this


-- faces-config
    <managed-bean>
        <managed-bean-name>StringVal</managed-bean-name>
        <managed-bean-class>java.lang.String</managed-bean-class>
        <managed-bean-scope>request</managed-bean-scope>
    </managed-bean> 

-- bean
    @Equals(value = "#{StringVal}")
    private String password;

-- screen
  password <h:inputText value="#{bean.password}" label="password" id="password"/>
  Retype <h:inputText value="#{StringVal}" label="retype" id="retype"/>

If you try this, you receive the message that the values are different, also when you put the same value in the fields. The cause for this behavior is that the cross field validation isn't implemented with this kind of usage in mind.

The cross field validation functionality supposes that the EL you have specified, contains always at least a base object and a property. It doesn't recognize that the same EL is used on the field, and thus during validation, the #{StringVal} is evaluated and not the screen value is taken. But the EL expression evaluates always to null, resulting in a difference in all cases. But there is a solution for this situation. You can place the bean scope in the EL expression. When you write #{requestScope.StringVal} in the managed bean (within the Equals annotation) and in the screen, the code behaves as expected. By the way, adding the bean scope is resulting in a better performance when the EL expression is resolved.
 

Model aware version

The cross field validation feature can use the entered value in the screen, but there is also a model aware version. In that case, the EL is evaluated when the system sees that the same expression isn't used on some inputText decoded in the same request. In this situation, the bean scope isn't required to be specified.
 

This is a possible use case: when we have defined in the faces-config a bean of type Date with the name startDate. It can contain some date, initialized by some mechanism that is used to mark the oldest date possible for a period. When the user enters a date in the screen, we can verify it is later then the value in the faces-config defined value. Here we don't need to specify the bean scope, although it is allowed.
 


-- faces-config
    <managed-bean>
        <managed-bean-name>startDate</managed-bean-name>
        <managed-bean-class>java.util.Date</managed-bean-class>
        <managed-bean-scope>application</managed-bean-scope>
    </managed-bean> 

-- bean
    @DateIs(valueOf = "#{startDate}", type = DateIsType.after)
    private Date beginDate;

-- screen
  Begin date <h:inputText value="#{bean.beginDate}" label="Begin date" id="beginDate"/> <br/>

Methods with unified expression language
 

With the Unified Expression Language, you can specify method calls in EL with (or without) parameters on containers that support EL 2.2. But these constructs can't be used in the value attribute of an input field.

<h:inputText value="#{methodBean.doMethod()}" label="method" id="method"/> <br/>

The above construct shows the return value of the method when the screen is presented. But when submitting the page, you receive an exception (from JSF, not from ExtVal) saying that it can't find the property doMethod. After all, it searches a method setDoMethod with a single parameter.
 

Now we can continue our theoretical example to see how far we can go. If we do create that required method (and you have a use case in which you can set the value on the same place then you retrieved it with the doMethod), how should the expression that we put in the ExtVal annotation look like?
 


    @Equals(value = "#{methodBean.doMethod()}")
    private String source;


This format isn't working, because the ProcessedInformationRecorder can't understand method constructs in EL. So the cross field validation performs the model variant, but doesn't see the newly typed text on the screen. And without the brackets, it interprets it as a property and looks for the method getDoMethod.
So is this an issue of ExtVal? I don't think it because I never encountered a possible use case that requires an EL method expression in an inputText tag.
Can't we use then the new features of the Unified Expression Language? Yes it is possible, as we already mentioned in the previous section. If the container supports the UEL, ExtVal uses the result of the expression to compare it to the screen entered value.
 

Conclusion
 

I have presented here some rare use cases of cross field validation and how ExtVal reacts on them. As we saw, the feature can be used without the usage of a managed bean, as long as we specify the bean scope. And with the newly introduced method expressions in UEL, you are able to use it in the model aware variant. And thus can be used to retrieve the value from other resources like JNDI. But with the upcoming new add-on, it will be easier since you have the pluggable reference resolver for the cross field validation.

Have fun with ExtVal