Wednesday, May 25, 2011

Using AJAX with Spring MVC 3.x

While working with one of my application, I had a simple requirement to grab customer data as they key-in their customer Id. First thought which came to my mind was to simply use AJAX however in the past we had to write lot of stuff and workarounds to make it work.

However, Spring 3.0 now officially support AJAX remoting with JSON as discussed in Keith Donald's article.

So, after reading this article the first step was to get jQuery and JSON javascript files which can be downloaded from their sites. This was easy!

Next, write the javascript code in the JSP page which activates on users input (to be specific on key up event). Here is how I did it:

First I veified that jQuery actually works on my page after adding following lines:
<script type="text/javascript" src="/travel-request/resources/js/jquery-1.4.min.js"></script>
<script type="text/javascript" src="/travel-request/resources/js/json.min.js"></script>
<script type="text/javascript">   
     $(document).ready(function() {
        console.log("Welcome to jQuery.");
      });
</script>

With the help of Firebug in FireFox I was able to verify using jQuery logging facility that it works!

So, half the job was done. Now comes the interesting part i.e. making an AJAX call to my customer information service and getting the response. Here is what I did:

Capture key up event and ignore any characters other than numbers i.e. 0-9
    $('#customerId').keyup(function(event) {
        var val = $(this).val();
        var regex = /[a-zA-Z\!@#%&()+{}?*+.^$ ]/g;
        if(val.match(regex)) {
            val = val.replace(regex, "");
            $(this).val(val);
        }
        if($('#customerId').val().length > 9 || $('#customerId').val().length == 8)
            clearCustomerData();
        else if ($('#customerId').val().length == 9) {
            userid_length = $('#customerId').val().length;   
            if (userid_length > 9){
                console.log('customer Id must be 9 numbers only...')
            }else
                {
                    getCustomerData();
                }
        }
    });


Next define implementation for getCustomerData() and clearCustomerData() functions which makes an AJAX call:
function getCustomerData() {
   $.getJSON("/customer/getInfo", { customerId: $('#customerId').val() }, function(customerAvailable) {
   if(customerAvailable.available) {
      $('#customerFirstName').val("customerAvailable.firstName");
      $('#customerMiddleName').val("customerAvailable.middleName");
      $('#customerLastName').val("customerAvailable.lastName");
   });
}

function clearCustomerData(){
   $('#customerFirstName').val("");
   $('#customerMiddleName').val("");
   $('#customerLastName').val("");

}
NOTE: $('#customerFirstName') above is linking the html label in the form.

At last, write a service using Spring magic which takes care of the rest as follows in your controller:
    @RequestMapping(value="/customer/getInfo", method=RequestMethod.GET)
    public @ResponseBody CustomerAvailable getCustomerName(@RequestParam String customerId){
        CustomerAvailable customer = new CustomerAvailable();
        customer.setAvailable(true);
        Map<String,String> customerData = new HashMap<String,String>();
        customerData.put("firstName", "FIRSTNAME");
        customerData.put("middleName", "M");
        customerData.put("lastName", "LASTNAME");
        return customer;
    }

Done! But, before I go don't forget to take a look at JSON formatter this is a nice tool to format unstructured JSON response and make it readable. I've added a link to this tool in references.

References:
https://src.springframework.org/svn/spring-samples/mvc-ajax/
http://blog.springsource.com/2010/01/25/ajax-simplifications-in-spring-3-0/
http://api.jquery.com/jQuery.getJSON/
http://ajaxian.com/archives/jquery-logging
http://stackoverflow.com/questions/3403574/no-results-message-on-jquery-autocomplete
http://stackoverflow.com/questions/2866108/jquery-keyup-illegal-characters
http://jetlogs.org/2007/09/14/jquery-textbox-validation-and-the-blur-event/
http://jsonformatter.curiousconcept.com/

Tuesday, May 24, 2011

Binding Boolean properties in Spring MVC forms

While designing Spring MVC forms you might often encounter bind errors such as follows:

Failed to convert property value of type [java.lang.String] to required type [java.util.Date] for property 'updateStamp'; nested exception is java.lang.IllegalArgumentException:xxxxxx

Usually these errors are the result of Spring not being able to bind java primitive Wrapper classes like Integer, Boolean, etc. or user defined types such as Customer, to <form:input> or other form fields.

However, this issue can be resolved simply by registering custom property editors with your spring controller. Spring by defaults provides you with set of built-in property editors, that you can use in your controller. For user defined types, you must create your own.

As you know, Spring 3.x makes writing your controller a lot easier and here is how you register customer property editor within your controller:

In the example below, I've added built in property editors for Date, String, and Boolean.
    @InitBinder
    public void setPropertyBinder(WebDataBinder dataBinder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy");
        dateFormat.setLenient(false);
        dataBinder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
        dataBinder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
        dataBinder.registerCustomEditor(Boolean.class, new CustomBooleanEditor(false));
    }

One important note: In some cases specifically for Boolean properties you may encounter following error even after you have registered custom property editor for it.

org.springframework.beans.NotReadablePropertyException: Invalid property 'newCustomer' of bean class [org.sample.domain.customer.Customer]: Bean property 'newCustomer' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?

This error is coming from your Customer domain object. Most likely, newCustomer property is defined as follows:
public class Customer {
    private Boolean newCustomer;

    public Boolean getNewCustomer() {
        return newCustomer;
    }

    public void isNewCustomer(Boolean newCustomer) {
        this.newCustomer = newCustomer;
    }
   
}

If you change the method name from isNewCustomer() to getNewCustomer() the above mentioned error will disappear. This is because a standard java bean convention is to use set/get for property methods.

Thursday, May 19, 2011

Securing Java Applications with Spring Security

Since there are so many post dedicated to the subject of this post. I will be collecting the articles and posting here as and when I come across them.

Java Web Application Security - Part I: Java EE 6 Login Demo

Java Web Application Security - Part II: Spring Security Login Demo