Skip to main content

Ajax Form Validation Using Spring and DWR, Revised

December 4, 2007

{cs.r.title}







In " "http://java.net/pub/a/today/2007/02/07/ajax-form-validation-using-spring-and-dwr.html">Ajax Form Validation Using Spring and DWR," I presented a design
to use Ajax to perform client-side validation by invoking an
application's server-side validation logic. The whole point was to
easily leverage validation logic on the server to prevent
duplication of code, to reduce the time and cost of development and
maintenance, and to enhance the usability of a web application. Few
non-trivial software designs get it right the first time. This design has been
running in production systems for over a year (its publication is approaching the ten-month mark), and
has shown some limitations. This article discusses the emerged
limitations and then presents an improved design that not only
addresses the limitations but provides added functionality.
Conceptually, the two designs are very similar, and a familiarity
with the previous article, or working knowledge of Ajax, "https://dwr.dev.java.net">DWR, and the "http://www.springframework.org/">Spring Web MVC framework, are
assumed.

Before diving into the details, let's review the general flow of
an Ajax validation request. When the user changes an input field,
JavaScript is used to collect and bundle the necessary form input
and transmit it to the server. On the server, some validation
process is invoked. The results are returned to the browser, where
they are interpreted and used to update the browser's DOM to
visually display or hide validation messages. The browser's Ajax
call is essentially asynchronously submitting a custom form, a
JavaScript callback function is used to handle a custom response,
and the custom response is used to dynamically update the page.

Reasons for Change

The previous design had limitations. To support being invoked
through reflection, a design format was imposed on the Spring
Validators. While creating a separate validation
method for each input field is a good approach, on complex forms it
lead to a high number of short and therefore seemingly cluttered
methods. Many readers provided feedback that they, for one reason
or another, could not or would not refactor their existing
Validators.

Second, the original design contained a fair amount of redundant
boilerplate and hardcoded configuration. Every Ajax validated form
had to have its Validator's DWR proxy configured in
the dwr-config.xml file, which became unwieldy in large
applications. The JavaScript used to invoke validation had DWR
proxy names embedded in the script, which required the JavaScript
to be customized each time it was put in use on a new form.

Third, only single input field validation was supported. While
this was acceptable for the vast majority of requirements, the fact
remained that there are situations where validation of input is
more complex and validation of a "dependent" input field requires
the "parent" input field(s) to provide context to the validation
process. Examples of this situation include validating that a
user's zip code exists within their state (does 55431 exist in
MN?), that a credit card number belongs to the specified card type
(does the card number conform to the format of a Visa card?), and
that a password confirmation input field matches what the user
entered in the password input field. Since only a single input
field was submitted for validation at a time, this type of complex
validation was not possible.

Fourth, the previous design contained custom code that
duplicated functionality available in the Spring and DWR
projects.

With these weaknesses in mind, the goals of a redesign were to
simplify the architecture, minimize configuration, replace custom
code with existing Spring and DWR features, and support validation
of multiple input fields.

Server-Side Validation Using Spring

The pain of having to configure a DWR proxy for every
Validator led to the realization that the use of a
single front controller would be a great improvement over a front
controller per form. This would eliminate the majority of
the configuration in both the Java and JavaScript layers and would
lead to a much more reusable solution.

Because every Ajax validation request now runs through a single
front controller, this front controller must be smart enough to be
able to determine which Spring Web MVC Controller
would have been used to handle a traditional submission from the
same form. Once this Controller is known, Spring's
flexible and powerful API provides access to all the functionality
needed to dynamically perform the validation process.

Before we go into the details of how this is accomplished, let's
quickly review how Spring Web MVC works. When configuring Spring
Web MVC for use within your application, the
DispatcherServlet (or similar) is configured to know
which Controller is used to process a request for a
given URI. A typical dispatcherServlet.xml configuration
file contains something similar to:

[prettify]...
<bean name="/shop/newAccount.do" class="AccountFormController">
    <property name="petStore" ref="petStore"/>
    <property name="validator" ref="accountValidator"/>
    <property name="successView" value="index"/>
</bean>
...
[/prettify]

These mappings tell Spring Web MVC that when it receives a
request URI of /shop/newAccount.do to delegate execution to the
AccountFormController.

The core concept of the revised middle tier is that if the Ajax
validation request included the value of the form's action
attribute, the validation front controller could use the
DispatcherServlet's configuration mappings to
programatically determine which Controller would be
used to handle a normal form submission. Just as when the
DispatcherServlet receives a traditional form
submission to an action URI of /shop/newAccount.do and delegates
to the AccountFormController, the validation front
controller can determine that a validation request that includes
/shop/newAccount.do should be handled by
AccountFormController as well. The following code from
SpringAjaxController does just that:

[prettify]...
protected BaseCommandController getController(String actionUri) {
    BaseCommandController baseCommandController = null;

    try
    {
        // Attempt to find the controller by bean name
        baseCommandController = (BaseCommandController)
                                applicationContext.getBean(actionUri);
        LOG.debug("Found baseCommandController (by bean name): " +
                                            baseCommandController);
    } catch (NoSuchBeanDefinitionException nsbde) {
        LOG.debug("BaseCommandController not found by bean name, " +
                            searching AbstractUrlHandlerMappings");
    }

    if (baseCommandController == null) {
        Map<String, AbstractUrlHandlerMapping> map =
        applicationContext.getBeansOfType(AbstractUrlHandlerMapping.class);

        for (String mappingName : map.keySet()) {
            AbstractUrlHandlerMapping mapping = map.get(mappingName);

            Map<String, BaseCommandController> handleMap =
                                            mapping.getHandlerMap();
            baseCommandController = handleMap.get(actionUri);

            if (baseCommandController != null) {
                LOG.debug("Found baseCommandController (by " +
                "AbstractUrlHandlerMapping): " + baseCommandController);
                break;
            }
        }
    }

    return baseCommandController;
}
...
[/prettify]

Although updated to take advantage of advanced Spring
functionality, from this point the new design follows the same
general process as the old design: dynamically instantiate an
instance of the command class, bind the request input to the new
command class instance, invoke all Validators
associated with the Controller, and return the results
to the browser. This code is also found in the
AccountFormController:

[prettify]...
LOG.debug("Instantiating command");
Class<Object> commandClass = controller.getCommandClass();
Object command = BeanUtils.instantiateClass(commandClass);

LOG.debug("Populating command object with request values");
BeanWrapper beanWrapper = new BeanWrapperImpl(command);
beanWrapper.setPropertyValues(nameValuePairsMap);

LOG.debug("Invoking validators");
String commandName = controller.getCommandName();
Errors errors = new BindException(command, commandName);
Validator validators[] = controller.getValidators();

for (Validator validator : validators) {
    ValidationUtils.invokeValidator(validator, command, errors);
}

// For each input, get the first error message and assemble
Locale locale = LocaleContextHolder.getLocale();

LOG.debug("Building output");
for (String inputId : nameValuePairsMap.keySet()) {
    String inputValue = nameValuePairsMap.get(inputId);
    String unqualifiedInputId = StringUtils.unqualify(inputId);
    String args[] = { unqualifiedInputId, inputValue };

    String message = validationMessageFormatter.getFieldErrorMessage(errors, inputId, args, locale);
    if (message == null) {
        // There was no validation message, return an empty String
        message = "";
    }

    resultMap.put(inputId, message);
}

return resultMap;
[/prettify]

After instantiating an instance of the command class, the input
values from the browser must be bound to this instance. Although
the binding process is still manual, in the sense that Spring Web
MVC does not do it for us, the use of Spring's
BeanWrapper and BeanWrapperImpl concrete
implementation take care of all the actual work, including the binding
of multiple inputs. Because the BeanWrapper supports
nested paths, the input id and values transmitted from the browser
can be used to populate complex domain objects that back the form.
For example, if the validation request contained
"user.firstname=Ted&user.lastname=Anderson&user.address.zipCode=55311",
the BeanWrapper would follow JavaBean naming
conventions and would set the firstname property of a
User object to the value of Ted,
the lastname property to a value of Anderson, and
55311 would be set to the zipCode property of the
User's underlying Address object.

With the service layer to process validation messages in place,
we next need to discuss how communication with the server has been
revised. In the next section, you will see how DWR is used to
expose and invoke the validation front controller.

Ajax Using DWR

As with the previous design, Direct Web Remoting (DWR) is used
for Ajax communication between the client and middle tiers. DWR
works by dynamically generating JavaScript based on Java classes.
Using a servlet and the necessary JavaScript infrastructure,
JavaScript calls made in the browser are transparently sent to the
server and invoked on the Java class, with the results returned to the
browser and available in JavaScript. This is how server-side logic--in this case, Spring Web MVC validation logic--is exposed to
the client through Ajax.

With the goal of simplifying configuration, the updated design
takes advantage of the newly released DWR 2.0 and its optional
annotation-based configuration. Written by Maik Schreiber, the
annotations can be used as a replacement or in combination with the
dwr.xml configuration file used in DWR 1.x. By using
annotations, the problem of a large and potentially bloated
dwr.xml configuration file can be avoided.

As described in the DWR
annotation documentation
, configuring your application to use
DWR annotations is a three-step process. First, you must specify
the DWR controller servlet in your web.xml file. Second, the
fully qualified class name of each class that is to be exposed
through DWR needs to be added to a comma-separated list in the
web.xml file. Third, each one of these classes needs to be
decorated with DWR annotations. To do this, begin by decorating
each class with a @RemoteProxy annotation. By default,
the class name is used as the JavaScript scripting name (the name
of the JavaScript proxy object). It's good practice to expose as
little information as possible about the Java layer in JavaScript,
and the name attribute of the @RemoteProxy annotation
is used to explicitly specify the scripting name of the JavaScript
proxy object. Because the class we want to expose to JavaScript is
a Spring-managed bean, DWR also needs to be instructed to use a
SpringCreator and to locate the bean in Spring's
ApplicationContext by a specific name. Finally,
decorate each method to be made available through remote access
with a @RemoteMethod annotation. Naturally, those
methods without this annotation will not be remotely accessible.
Let's see an example of the updated
SpringAjaxController:

[prettify]@RemoteProxy(name="AjaxFormValidatorJS",
             creator=SpringCreator.class,
             creatorParams = @Param(name = "beanName",
                                    value = "ajaxFormValidator"))
public class SpringAjaxController implements ApplicationContextAware
{
...
@RemoteMethod
public Map<String, String< validateString(String formActionUri,
                                        String inputIdValuePairs)
{
    Map resultMap = new HashMap();
    BaseCommandController controller = getControler(formActionUri);
    ...
}
...
}
[/prettify]

You can see that we're instructing DWR to use a
SpringCreator, that we want to locate the bean by a
bean name, and the bean name we're looking for in the Spring
ApplicationContext is ajaxFormValidator. DWR will
generate a JavaScript object named AjaxFormValidatorJS
to proxy the Java-based instance on the server. This object will
have a validateString() function that when invoked
executes the method of the same name on the server.

It's worth noting that decorating classes with DWR annotations
creates an otherwise unnecessary compile-time dependency between
the classes and DWR. This could become problematic if you want to
reuse your Java layer on a project or in an environment where DWR
cannot be used. While this will mostly likely be a small and
academic issue for most developers, it's still a design issue to be
aware of.

Once the validateString() method has completed the
Map of validation message(s) must be returned to the
browser in a format usable in JavaScript. The previous design
manually assembled the results into a String of
name-value pairs and transmitted it to the browser, where it was
manually parsed. Not only providing an excellent mechanism to
invoke Java from JavaScript (and vice versa), DWR also assists in
converting Java objects into a representation that can easily be
used within JavaScript. Because the validateString()
method returns a Map, DWR will use a built-in
Converter to transparently convert the map into a
JavaScript array. As you'll see in the next section, this array
will be used to by a callback method to dynamically update the page
with validation error messages.

Controlling the Process with JavaScript

Although it has been updated to include the ability to submit
multiple input id and value pairs, to eliminate hardcoded DWR proxy
references, and to include the value of the form's action attribute
in the Ajax request, conceptually the JavaScript used to support
Ajax validation functions the same as the original version. Each
Ajax-validated form input has an onChange() event
listener registered that will invoke the validate()
function when the value of that input field has been changed.

Let's look at the details:

[prettify]<script type='text/javascript'>

function validate(inputArray) {
    var request = "";
    var formAction = "";

    if (inputArray.length == null) {
        <%-- Submit a single input field and value --%>
        request = formatInput(inputArray.id, inputArray.value);
        formAction = getFormAction(inputArray.id);
    } else {
        <%-- Submit multiple input field and value pairs --%>
        for (var i = 0; i < inputArray.length; i++) {
            var input = document.getElementById(inputArray[i].id);
            var nameValuePair = formatInput(input.id, input.value);

            request = request + nameValuePair;

            if (i != inputArray.length) {
                request = request + "&";
            }
        }

        formAction = getFormAction(inputArray[0].id);
    }

    <%-- Invoke server-side logic --%>
    AjaxFormValidatorJS.validateString(formAction,
                                       request,
                                       handleValidationResponse);
}

function handleValidationResponse(response) {
    <%-- The response is an array of id/message value pairs --%>
    for (inputId in response) {
        var errorElementId = inputId + ".errors";
        var validationMessage = response[inputId];

        dwr.util.setValue(errorElementId, validationMessage);
    }
}

function getFormAction(inputId) {
    var currentElement = document.getElementById(inputId);

    while (currentElement != null) {
        if (currentElement.tagName.toLowerCase() == "form") {
            <%-- Drop the http://servername --%>
            var formAction = "";
            var locationFragements = currentElement.action.split("/");

            for (var i = 4; i < locationFragements.length; i++) {
                formAction = formAction + "/" + locationFragements[i];
            }

            return formAction;
        } else {
            currentElement = currentElement.parentNode;
        }
    }
}
...
[/prettify]

The validate() function is responsible for
initiating the validation process and its input will be either a
single form input element or an array of form input elements. The
length property of the argument will indicate if the
input is a single input element or an array of input elements. In
the case where the argument is a single form input element, the
element's id and value are assembled into a single name-value pair.
In the case where the argument is an array of form input elements,
the id and input values of each element in the array are assembled
into single a name-value pair. Regardless of which case is
executed, the end result is a String containing
id-value pairs of each form element that will be transmitted to the
server for validation. It is this ability to process either an
individual element or an array of elements that provides
client-side support for multi-input validation.

As we've seen from previous sections, the server-side validation
logic needs the value of the form's action attribute
included in the request. Looking at the previous sample code, the
getFormAction() function starts from the input element
that fired the onChange() event and upwardly navigates
the browser's DOM until the form element is found.
With a reference to the form element, the form's
action attribute is read and then included as part of
the validation request transmitted to the server. By dynamically
determining the value, the JavaScript is much more flexible and
reusable. As such, this script will work with any form and can be
globally included on all pages (tools such as Tiles or SiteMesh
makes this particularly easy).

Using the DWR-generated AjaxFormValidatorJS object,
the request String (composed of the input name-value
pairs and form action attribute value) is sent to the server for
validation and the browser awaits a response.

When the response is returned from the server, the
handleValidationResponse() callback function is
invoked with the sever-generated array of DOM element id and
validation messages. Every input element sent to the server will
have a corresponding entry in the response array. The
handleValidationResponse() function iterates through
the array and passes the element id and validation message to DWR's
dwr.util.setvalue() function, which updates the
particular element with the validation message. Browser DOM changes
take effect immediately and it is this show/hide effect that gives
the appearance of client-side validation and greatly enhances the
user experience of the web application.

Conclusion

This article discussed weaknesses in a previously presented
design allowing easy integration of Ajax form validation into web-based applications. After discussing the weaknesses, an evolved but
simplified design was presented that used a single front
controller, DWR annotations and Converters, and updated JavaScript
to dramatically reduce configuration and maximize reusability. By
replacing custom code with Spring functionality, the code is not
only simplified but now supports multiple input validation.
Finally, by no longer imposing a design format on Spring
Validators, existing application code can be Ajax-enabled without refactoring.

Resources


width="1" height="1" border="0" alt=" " />
Eric Spiegelberg is a Minneapolis-based Java/EE consultant specializing in Spring, Hibernate, and web-based software development.
Related Topics >> Programming   |   Web Development Tools   |   

Comments

Hi, Unfortunately the

Hi, Unfortunately the download link is broken. Can you maybe provide an alternative location for the sources archive? Regards, Sebastian