Subscribe via Feed

Writing Your Own Complex Custom Validator -- JSF Style

Jeremy Hodge, Aug 6, 2010, 5:45:30 PM

NOTE: This example builds upon my previous article "Writing a Managed Bean to Automate Server Side Functionality in XPages." I will be expanding upon topics covered there, and you may want to (re-)read that article before continuing with this one.

If you have started writing your own XPages applications, by now you have needed to use validators to check the integrity and validity of the data your user submits. XPages provides several built-in validators to validate things like required fields, a simple value range, etc.

But sometimes, you need to write a complex validator to maybe look some data up, compare it, check document status, etc. Cases in which a simple validator is just insufficient.  This is where custom validators come into play.

There are three basic ways to implement a custom validator, all are located in the data section of the All Properties tab of your input control.

1. The "validator" property
2. In the "validators" property where you can add an "xp:validator"
3. In the "validators" property you can add an "xp:customValidator"

The validator (#1) and the xp:customValidator (#3) are basically the same thing, except the custom validator (#3) can be used within the validators to provide multiple validators against a single object.

Let's discuss #1 first. The "validator" property.  The validator property represents a Method Binding that performs the validation. Do you remember from my previous post what a Method Binding is?  Well, its basically nothing more than a "pointer" to a function. It basically "binds" or connects a method to an object to perform a specific function, in this case, validation.

You know and use bindings EVERYWHERE in XPages.  For example, another type of a binding is a value binding, where you bind the value of a field to a specific document and value. This takes the form of value="#{dataSource.fieldName}" in your XPage source code. That's an Expression Language expression that connects your field to your input control through the use of getFieldName and setFieldName functions. A method binding looks exactly the same. To connect a method (or function or procedure by other names) to a validator, you use an EL expression like this #{ObjectContainingMethod.methodName} ... which basically says on the object named ObjectContainingMethod, call the function named "methodName"

So let's get to an example. We're going to implement our first validator in a managed bean. I'm going to assume you have already implemented a managed bean or are familiar with the concept. To review the managed bean topic, re-read my previous post on them here.

Let's expand upon our HelloWorld example from the last post, and add a function that will serve as a validator for an input text field. To our HelloWorld class, lets add some more code.

First let's import a few types into our example:

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;

and let's add a new method block:

public void validate(FacesContext context, UIComponent component, java.lang.Object value) {

         // Your validation code goes here

}

Every validator must accept three parameters, the current context, the component being validated, and the value being validated, and must return "void" (or "nothing" for your new java programmers).

Now we can add some actual validation code to our method.  Here's a simple validation that takes the value of the field, and verify a document exists in a view that is keyed to contain the values the user can enter. If the document for the specified value is found, a status check is done against another value of the retrieved document, further validating the integrity of the data.

Since we are going to be doing some Notes-related lookups, we need to add a few imports for domino specific types:

import lotus.domino.Database;
import lotus.domino.Document;
import lotus.domino.NotesException;
import lotus.domino.View;

Now, our expanded method

    public static void validate(FacesContext context, UIComponent component, java.lang.Object value) {

        View lookupView;
        Document validationDocument;
        
        try {
            JSFUtil jsfUtils = new JSFUtil();
            
            Database database = jsfUtils.getCurrentDatabase();
            
            if (database != null) {
                lookupView = database.getView("validValues");
                
                if (lookupView != null) {
                    validationDocument = lookupView.getDocumentByKey(value);
                    if (validationDocument != null) {
                        if (validationDocument.getItemValueString("FormStage").equalsIgnoreCase("Active")) {
                            // I can do more here if I like.
                        } else {
                            throw new javax.faces.validator.ValidatorException(new javax.faces.application.FacesMessage("Invalid Code (" + value + ")"));
                        }
                        validationDocument.recycle();
                    } else {
                        throw new javax.faces.validator.ValidatorException(new javax.faces.application.FacesMessage("Invalid Code (" + value + ")"));
                    }
                    lookupView.recycle();
                } else {
                    throw new javax.faces.validator.ValidatorException(new javax.faces.application.FacesMessage("Invalid Code (" + value + ")"));
                }
            } else {
                throw new javax.faces.validator.ValidatorException(new javax.faces.application.FacesMessage("Invalid Code (" + value + ")"));
            }
                
        } catch (NotesException e) {
            throw new javax.faces.validator.ValidatorException(new javax.faces.application.FacesMessage("An unknown Exception Occurred processing Code (" + value + ")"));
        }
    }

Basically the method executes until you find a condition upon in which you decide the value has failed validation. When you decide that, you throw a specific type of an error object, the javax.faces.validator.ValidatorException. As a parameter to that object, you pass a FacesMessage object, which is the text that appears in the xp:errorMessage or sp:errorMessages control when the validation fails. For example, the sample code would create the following message, displayed to the user:

Now, to implement our validator, save your managed bean, and in the xp:inputText of our Hello World example, edit your validator property, change the language to Expression Language, and set the value to HelloWorld.validate.  Add a view to your database called "validValues" and key it off of a field and populate the view with documents, and you have your validator!

You can do the same thing for #3, the xp:customValidator under the validators property ...

The other option for creating a custom validator is to create a validator object, and load it through the faces-config.xml file. The difference here is you create a single class that contains only your validator. The benefit to implementing individual validator objects is that you can re-use them throughout your application without having to add them to different managed beans, or you can use them even if you aren't using a managed bean.

To create it, add a new Class to your package, and code it like this:

package com.ZetaOne.MyFirstBeans;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;

import lotus.domino.Database;
import lotus.domino.Document;
import lotus.domino.NotesException;
import lotus.domino.View;

import com.ZetaOne.util.JSFUtil;

public class myValidator implements Validator {
    public void validate(FacesContext context, UIComponent component, java.lang.Object value) {

      View lookupView;
        Document validationDocument;
        
        try {
            JSFUtil jsfUtils = new JSFUtil();
            
            Database database = jsfUtils.getCurrentDatabase();
            
            if (database != null) {
                lookupView = database.getView("validValues");
                
                if (lookupView != null) {
                    validationDocument = lookupView.getDocumentByKey(value);
                    if (validationDocument != null) {
                        if (validationDocument.getItemValueString("FormStage").equalsIgnoreCase("Active")) {
                            // I can do more here if I like.
                        } else {
                            throw new javax.faces.validator.ValidatorException(new javax.faces.application.FacesMessage("Invalid Code (" + value + ")"));
                        }
                        validationDocument.recycle();
                    } else {
                        throw new javax.faces.validator.ValidatorException(new javax.faces.application.FacesMessage("Invalid Code (" + value + ")"));
                    }
                    lookupView.recycle();
                } else {
                    throw new javax.faces.validator.ValidatorException(new javax.faces.application.FacesMessage("Invalid Code (" + value + ")"));
                }
            } else {
                throw new javax.faces.validator.ValidatorException(new javax.faces.application.FacesMessage("Invalid Code (" + value + ")"));
            }
                
        } catch (NotesException e) {
            throw new javax.faces.validator.ValidatorException(new javax.faces.application.FacesMessage("An unknown Exception Occurred processing Code (" + value + ")"));
        }
    }
}

Now, in your faces-config.xml we must define the validator. To do this we add a new validator node, give it an ID, and tell it what class to use.

 
      codeValidator
      com.ZetaOne.MyFirstBeans.myValidator
 

Now, under the validators property of our input text field, we can specify the name we used in the node above, codeValidator, like this:

And wallah, we now have a coimplex validator that we can use anywhere on any validatable object!

I hope this helps you better understand how to accomplish complex validation in your XPages application, and as always, HAPPY CODING!



3 responses to Writing Your Own Complex Custom Validator -- JSF Style

daniel daniel, April 27, 2011 at 8:08 PM

Hi

I am trying to implement this one but I am getting error as "The import com.ZetaOne.util.JSFUtil cannot be resolved ".

Where can I find this class!

Thanks in advance


Jeremy Hodge, December 18, 2010 at 3:22 PM

Looks like you have some other errors in your code ... Im not sure but those errors don't really like up with the code as I can see it. Can you post your code to review? Also, the procedure above is strictly an example. It uses views and so forth from an "imaginary" database .... You'll have to adjust accordingly of course.


fenaten, December 18, 2010 at 2:22 PM

Trying to implement the "validator" property, after I inserted yout code, I got the following errors:
Multiple markers at this line
- Syntax error on token "FacesContext", = expected after this
token
- Syntax error on token(s), misplaced construct(s)
- Syntax error, insert "enum Identifier" to complete
EnumHeader
- Syntax error on token "void", @ expected
- Syntax error on token "UIComponent", = expected after
this token
- Syntax error on token ",", . expected

Is it because I do not have the JSFUtil object?