Monday, 29 April 2013

Spring MVC, Ajax and JSON Part 1 - Setting The Scene

I've been thinking about writing a blog on Spring, Ajax and JSON for a good while, but I've never got around to it. This was mainly because it was quite complicated and the technique required has been in a state of flux. When I decided to write this blog, I had a scout around the Internet and if you look on places such as Stack Overflow you'll see many different and often contradictory answers to the question "how do I write a Spring Ajax/JSON application?" I think that this is fault of the Guys at Spring in that they've been really busy improving Spring's support for JSon; not only that the Guys at JQuery have also been busy, which means that overall things have changed dramatically over the last couple of years and the answers to this "how do I write a Spring Ajax/JSON application?" are out of date.

If you take a look at Keith Donald's original Spring 3 MVC Ajax application you'll see that it's hideously complex. There's tons of boiler plate code and several hacky bits of JavaScript required to support JSON integration. In the latest release of Spring, all that's changed; as I said the Guys at Spring and JQuery have been busy and things are now a lot simpler.

When writing this kind of application there's a few steps to consider. Firstly, you need to load a page that's capable of making an Ajax request into a browser. Secondly, you have to write some code to service the Ajax request and, last of all, the page has to present its results.

In order to demonstrate Spring MVC, Ajax and JSON I'm going to use a shopping web site scenario. In this scenario when the user clicks on the eCommerce page link, the app loads some the items from the catalogue and displays them on the page. The user then checks a number of items and presses 'Confirm Purchase'. Now, this is where Ajax and JSON comes in, on pressing 'Confirm Purchase' the browser makes an Ajax request to the server sending it the item ids. The server then retrieves the items from the database returns them as JSON to the browser. The browser then processes the JSON, displaying the items on he screen.

In writings the code, the first step is to create a Spring MVC project using the project templates available on the Spring Dashboard.


Once you have a blank project there are a couple of changes that you need to make to the project's POM file. Firstly you need to add in the Jackson JSON Processor dependencies. Next, you need to update the version of Spring to 3.2.2. This is because the template project generator still produces a version 3.1.1 project.

<dependency>
               <groupId>com.fasterxml.jackson.core</groupId>
               <artifactId>jackson-core</artifactId>
               <version>2.0.4</version>
          </dependency>
          <dependency>
               <groupId>com.fasterxml.jackson.core</groupId>
               <artifactId>jackson-databind</artifactId>
               <version>2.0.4</version>
          </dependency>

<org.springframework-version>3.2.2.RELEASE</org.springframework-version> 

If you look at my code, available on Github, you'll see that I've also added the Tomcat Maven plugin and set the Java compiler source/target versions to 1.7. These are both optional steps.

The next thing to do is to create an Item class to define items that the user will purchase from our imaginary catalogue.

public class Item {

 
private final int id;

 
private final String description;

 
private final String name;

 
private final BigDecimal price;

 
private Item(int id, String name, String description, BigDecimal price) {
   
this.id = id;
   
this.name = name;
   
this.description = description;
   
this.price = price;
 
}

 
public final BigDecimal getPrice() {

   
return price;
 
}

 
public final int getId() {

   
return id;
 
}

 
public final String getDescription() {

   
return description;
 
}

 
public final String getName() {

   
return name;
 
}

 
public static Item getInstance(int id, String name, String description, BigDecimal price) {

   
Item item = new Item(id, name, description, price);
   
return item;
 
}

}

The code above defines our simple Item. It's attributes include id, name, description and price.

The next step in this shopping scenario is to write the code that displays the items on the screen, so that the user can make his/her selections and submit them to the server. As you might expect this involves writing a JSP that includes a form and handling the request from the form with a Spring MVC controller. I'm going to talk about the controller code first because it determines how the JSP is written.

  /**
   * Create the form
   */
 
@RequestMapping(value = "/shopping", method = RequestMethod.GET)
 
public String createForm(Model model) {

   
logger.debug("Displaying items available in the store...");
    addDisplayItemsToModel
(model);

    addFormObjectToModel
(model);

   
return FORM_VIEW;
 
}

 
private void addDisplayItemsToModel(Model model) {

   
List<Item> items = catalogue.read();
    model.addAttribute
("items", items);
 
}

 
private void addFormObjectToModel(Model model) {
   
UserSelections userSelections = new UserSelections();
    model.addAttribute
(userSelections);
 
}

The controller method that gets our form displayed on the screen is createForm(…). This method is annotated by the usual RequestMapping annotation that tells Spring to map all GET requests with a URL of 'shopping' to this location. The method has three steps: it first reads the catalogue to obtain a list of items to display; then it creates a UserSelections object, which is used by the form when it submits the items a user has bought; and finally it directs us to the shopping.jsp.

These setup steps are pretty normal for this kind of form creation method: first you add the display data to the model and then you add the forms means of submission to the model; however, quite often these two steps are combined.

In the controller code you'll also see a catalogue object that's used to get hold of the items. In a real application this would be equivalent to creating a service layer component that reads data using a DAO and all the paraphernalia usually associated with this kind of application. In this case it simply creates a list of items from hardcoded arrays and is not important.

This controller code ties up very nicely to the JSP snippet below:

<form:form modelAttribute="userSelections" action="confirm" method="post">
               <c:forEach items="${items}" var="item">
                    <div class="span-4 border">
                         <p><c:out value="${item.name}" /></p>
                    </div>
                    <div class="span-8 border">
                         <p><c:out value="${item.description}" /></p>
                    </div>
                    <div class="span-4 border">
                         <p>£<c:out value="${item.price}" /></p>
                    </div>
                    <div class="span-4 append-4 last">
                         <p><form:checkbox value="${item.id}" path="selection"/></p>
                    </div>
               </c:forEach>
               <div class="prepend-12 span-4 append-12">
                    <p><input class="command"
                                             type="submit" name="action"
                                             value="Confirm Purchase"
                                             accesskey="A" /></p>
               </div>
          </form:form>

There are a couple of points to note here. Firstly, I'm making life easy for myself by using the Spring form tag (<form:form ... >) and secondly I'm using Blueprint to format my page. In setting up the form tag, the first thing to consider is the form tag's attributes: modelAttribute, command and method. modelAttribute is used bind the UserSelections class supplied by the controller to the HTML form. The command attribute is a URL that tells the browser where to submit its data and the method attribute tells the browser to POST the submission to the server.

In the next part of the JSP I've used a forEach loop to display the items previously retrieved from the catalogue. The important line here is the form:checkbox tag. This creates, as you may suspect, a HTML checkbox using the item's id and a selection 'path'. To me the term 'path' sounds confusing. What the guys at Spring actually mean is "on submission, take the value stored in the checkbox's value attribute (item.id) and if selected store it in the UserSelections object using the setSelection(…) method". This is done in the background probably by parsing the HttpServletRequest object and then doing some jiggery poker with Java reflection. The point to note is how the names in the JSP tie into the attribute names of the UserSelection class.

I've found the Spring form tag pretty useful in a large majority of cases; however, in making things simple it does have occasional limitations in what data it can bind to which HTML object. When you bump into one of these limitations then use more the verbose Spring Bind tag in conjunction with the form tag.

Okay, so when you run this code you get a screen displayed that looks something like this:


The thing is, I know that I haven't talked about AKAX and JSON in this blog, but I needed to set the scene. In the second part of this blog I'll definitely get down to implementing the nitty-gritty part of the scenario: obtaining and displaying JSON data via an Ajax call.


For the full source code to this blog, see GitHub - https://github.com/roghughe/captaindebug/tree/master/ajax-json

2 comments:

Unknown said...

How do you handle the errors (like form:errors) ?

Roger Hughes said...

I purposely omitted validation in this example as I wanted to keep things as clear as possible. Form errors can be handled using a validator, either the original Spring kind or the newer JSR303 kind. For more information, type 'validator' into the search box on the top right of the screen.