2010-01-31

REST with Spring 3.0, Spring MVC and Dojo. Part 2 - GET from Dojo perspective

In part 2 of the REST with Spring 3.0, Spring MVC and Dojo series we will take a look at the Dojo part of the story - that is how to read data via REST when you're a RESTful service client.

Operation REST method Sample URI
Return item(s) GET /books (all books)
/books/ (all books)
/books/12 (one book with ID = 12)
Create a new item POST (you pass the ID in the object itself)
/books
/books/
Update an item PUT (you pass the ID in the object itself)
/books
/books/
Delete an item DELETE /books/12 (book with ID = 12)

I will show you some very basic Dojo code so that you can get the idea. Dojo is a client side technology, written in JavaScript, and it doesn't care whether the service behind REST is implemented in Java or any other language.

Let's look at the sample code. Any HTML page can handle this, you don't even need a servlet container.

// I got the idea from an IBM tutorial.
function _request(method, xhrArgs) {
    if (!xhrArgs.url) {
        var deferred = new dojo.Deferred();
        deferred.errback();
        return deferred;
    }
    xhrArgs.url = xhrArgs.url;
    return dojo[method](xhrArgs);
}

This is a helper function to handle server requests that we will use for GET, PUT, POST and DELETE requests. It uses a Deferred object. Deferred is Dojo's way of handling "threads" and asynchronous matters. This is an important thing to mention: the calls we will make are asynchronous.

Now, let's see the actual code that gets stuff from the server using the function mentioned above. The first code snippet shows a function that gets all the books.

function retrieveBooks() {
    var deferred = _request("xhrGet", {
        url: "http://localhost:8080/restsample-0.0.1-SNAPSHOT/rest/books.json",
        handleAs: "json"
    });
    deferred.addCallback(this, function(value) {   
        // put retrieved picklists in select.options[]
        var books = value.books;
        alert(books.length + " books retrieved.");
        if (books.length > 0) {
            alert("The first retrieved book is: " + books[0].name);
        }
    });
    deferred.addErrback(this, function(value) {                
          alert("Error while retrieving books: "  + value);
    });
}

Okay, so that is a simple (and quite useless in real life because it only alerts the results) function that gets data using our previously defined _request function.

xhrGet is a Dojo function. It specifies that we want to call a GET method (and not a PUT etc.).

Next, we specify the address. Note that it ends in .json just in case the browser decides to get XML instead (even though we defined JSON as default).

Then we add callbacks, one for success (addCallback) and one for error handling (addErrback). Look how easily data is read, that's thanks to JSON. No evil eval, no explicit parsing. Why callbacks? Well, that's exactly because the call is asynchronous and we do not precisely know when we are going to get results.

The error callback will handle 500, 404 and other HTTP error codes. That's right. When a server side exception occurs the error callback is called.

Let us also look at a function that retrieves just one book instead of all:

function retrieveBook(bookId) {
    var deferred = _request("xhrGet", {
        url: "http://localhost:8080/restsample-0.0.1-SNAPSHOT/rest/books/" +
            bookId + ".json",
        handleAs: "json"
    });
    deferred.addCallback(this, function(value) {   
        // put retrieved picklists in select.options[]
        var book = value.book;
        alert("The retrieved book is: " + book.name);
    });
    deferred.addErrback(this, function(value) {                
          alert("Error while retrieving book: "  + value);
    });
}

It should now be self explanatory.

Download source code for this article

  1. REST with Spring 3.0, Spring MVC and Dojo. Part 1 - GET
  2. REST with Spring 3.0, Spring MVC and Dojo. Part 2 - GET from Dojo perspective
  3. REST with Spring 3.0, Spring MVC and Dojo. Part 3 - POST and JSR-303 validation
  4. REST with Spring 3.0, Spring MVC and Dojo. Part 4 - PUT (updating objects)
  5. REST with Spring 3.0, Spring MVC and Dojo. Part 5 - DELETE

2010-01-30

REST with Spring 3.0, Spring MVC and Dojo. Part 1 - GET

Introduction


I am going to write a few posts on RESTful application development with Spring 3.0, using Spring MVC, Dojo and Maven. In the first part, I will show you how to create a basic controller and handle a GET request. The rest: DELETE, POST, PUT we will cover later.

I would suggest that - in case you're not very familiar with REST - you read some information about REST, for instance A Brief Introduction to REST from InfoQ or this article on REST conventions.

I am going to use a Maven project in Eclipse Ganymede with the M2Eclipse plugin, the latest development build (0.9.9.200912160759), as the latest production release contains some weird bugs including this annoying bug that seems to be fixed in the development build (it's time to release, guys!).

I am also going to use JBoss 5.1.0 JDK6 and that's why I will put one of the Spring beans files (applicationContext.xml) in the classes folder of the output war which is ugly but it works (it didn't work when I put it in /WEB-INF/; it did work in WebSphere though). Funny that when I called it beans.xml I would actually get an exception from JBoss: javax.inject.DefinitionException: bean not a Java type. Handy, I guess we're getting somewhere with error reporting.

One more thing, I am using Java 1.6 but Java 1.5 will suffice for everything - except for validation where a Java 6 annotation will be used. It is possible to implement your own validation though, it's really up to you. Validation will be shown in the POST and PUT parts. In this post only very basic (type) validation will be shown.

What you will learn


Let us begin! We are going to handle GET requests - in this example two GET requests. One without parameters (it will return all books - books will be sample items in this example) and one with a parameter - book ID. This will allow you to see:

  • How to handle input RESTful parameters with Spring MVC
  • How to return a collection of items
  • How to return a single item

Operation REST method Sample URI
Return item(s) GET /books (all books)
/books/ (all books)
/books/12 (one book with ID = 12)
Create a new item POST (you pass the ID in the object itself)
/books
/books/
Update an item PUT (you pass the ID in the object itself)
/books
/books/
Delete an item DELETE /books/12 (book with ID = 12)

The controller


Let's take a look at our controller class - the controller will handle the requests.

package me.m1key.restsample.controllers;

import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
// Some imports excluded for brevity, eh?

/**
 * Books controller.
 *
 * @author Michal Huniewicz
 *
 */
@Controller
@RequestMapping("/books")
public class SampleController {
    // Methods excluded for brevity.
}

This is it, it doesn't have to implement or extend anything. It is only annotated as a controller with the @Controller annotation. It means it can have many actions, like MultiActionController in the old days.

It is also annotated with @RequestMapping. This is where we specify the path which this controlller is to handle. As you will see, the action methods will also be annotated with this to further narrow down the criteria.

Setup


Now, how will Spring know about this Controller? We will tell it specifying certain information, first in the web.xml file.

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Books REST Handler</display-name>

    <context-param>
        <param-name>webAppRootKey</param-name>
        <param-value>rest.root</param-value>
    </context-param>

    <!-- Servlets -->

    <servlet>
        <servlet-name>rest</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>rest</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
    
    <!-- Rest of the file omitted for brevity. -->

</web-app>

In the web.xml file we declare the dispatcher servlet from Spring and we ask the server to load it on startup. We also assign this servlet to a certain path, that is /rest/* preceded by application's context root.

Now, because we specified the servlet name to be rest, we will create one more file next to web.xml (in the /WEB-INF/ folder that is) called rest-servlet.xml. While web.xml is a standard JEE file, this rest-servlet.xml file is a Spring Framework kind of file.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:oxm="http://www.springframework.org/schema/oxm"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
                http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsd">

    <context:component-scan base-package="me.m1key.restsample.controllers" />
    
    <!-- Rest of the file omitted for brevity. -->



And this is how we tell Spring to search for components: with the context:component-scan element that specifies where to look for components in our application. An alternative would be to declare all the beans separately with the bean element.

Controller actions - return all books


What we did so far is we declared a controller, we told the server and Spring about it. It's time to write some actions.

package me.m1key.restsample.controllers;

import java.util.List;

import me.m1key.restsample.Factory;
import me.m1key.restsample.beans.BooksBean;
import me.m1key.restsample.to.BookTO;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

/**
 * Books controller.
 *
 * @author Michal Huniewicz
 *
 */
@Controller
@RequestMapping("/books")
public class SampleController {

    private static final String BOOKS = "books";

    /**
     * Returns all books.
     *
     * @return all books
     */
    @RequestMapping(value = "", method = RequestMethod.GET)
    public ModelAndView handleAllBooks() {
        ModelAndView mav = new ModelAndView();

        BooksBean booksBean = Factory.getBooksBean();

        List booksTO = booksBean.loadBooks();

        mav.addObject(BOOKS, booksTO);

        return mav;
    }

    // Rest of the code omitted for brevity.

}

Please take a look at the handleAllBooks method. It is annotated with @RequestMapping, like promised. That means that it will handle /books/ (or /books without the trailing backslash slash backslash slash) requests (within the app context root). What it returns is a ModelAndView object, that's it. We use BooksBean to get us the list of all books as controllers shouldn't really contain that kind of logic (see the MVC pattern). In our example BooksBean is just a dummy object, so it doesn't use any data source like it would in a real application.

There are two more things I must mention.

  • You should not call ModelAndView#addObject() more than once because if you do you might get inconsistent results depending on the output format (JSON/XML/...). To be precise, with JSON you would get all the objects no matter how many times you call addObject. With XML - just one. It looks like a bug but Arjen Poutsma was kind enough to explain to me that it isn't.
  • Wait a second, I'm talking JSON, XML, but the code just returns a ModelAndView object... That's right, the Controller is not aware of the output method. Ideally, we should be able to say /rest/books.json and get JSON response, /rest/books.xml and get XML response, /rest/books.html and get HTML response, /rest/books.mp3 and get a bunch of ladies singing the book titles. In this simple application we are going to handle only JSON and XML.

Telling Spring about the output methods we support - XML and JSON


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:oxm="http://www.springframework.org/schema/oxm"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
                http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsd">

    <context:component-scan base-package="me.m1key.restsample.controllers" />

    <bean
        class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
            <list>
                <bean id="mappingJacksonHttpMessageConverter"
                    class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
            </list>
        </property>
    </bean>
    <bean
        class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="mediaTypes">
            <map>
                <entry key="json" value="application/json" />
                <entry key="xml" value="application/xml" />
            </map>
        </property>
        <property name="defaultViews">
            <list>
                <bean
                    class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
                <bean class="org.springframework.web.servlet.view.xml.MarshallingView">
                    <constructor-arg>
                        <bean class="org.springframework.oxm.xstream.XStreamMarshaller"
                            p:autodetectAnnotations="false" />
                    </constructor-arg>
                </bean>

            </list>
        </property>
        <property name="defaultContentType" ref="jsonMediaType" />
        <property name="ignoreAcceptHeader" value="false" />
    </bean>

    <bean id="jsonMediaType" class="org.springframework.http.MediaType">
        <constructor-arg value="application/json" />
    </bean>

</beans>

Update 2010-03-31: Ralph Engelmann reported that the last bean definition is not valid as of Spring 3.0.1.RELEASE (see comments below). If you are using this version of later, you must define this bean in the following manner:
<bean id="jsonMediaType" class="org.springframework.http.MediaType">
    <constructor-arg value="application"/>
    <constructor-arg value="json"/>
</bean>
Thanks goes to Ralph.

Whew!

The first part (with AnnotationMethodHandlerAdapter) tells Spring which input methods we support (it will be useful when we send objects to the server via REST).

The second part (with ContentNegotiatingViewResolver) tells Spring which output methods we support (in this example, JSON and XML, as you can see).

Below we also define the default (preferred by us) output format. Please note that the client can still ignore it and request another one if available.

Controller actions - return one book by ID


This is going to be a bit more interesting than returning all the books for two reasons:
  • We must handle an entry parameter via REST (the requested book ID).
  • We must handle the situation when the requested book cannot be found.

Let's see the action method.

/**
     * Returns a single book by ID.
     *
     * @param response
     *            response
     * @param bookId
     *            book ID
     * @return single book
     */
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public ModelAndView handleBookById(HttpServletResponse response,
            @PathVariable("id") Long bookId) {
        ModelAndView mav = new ModelAndView();

        BooksBean booksBean = Factory.getBooksBean();

        BookTO bookTO = booksBean.loadBookById(bookId);

        mav.addObject(BOOK, bookTO);

        return mav;
    }

Now, with the new RequestMapping annotation we narrow down the query criteria. This mapping will handle /rest/books/1 kind of addresses (preceded by context root). Please note the @PathVariable annotation that binds the variable in the request mapping to the annotated variable.

See the response parameter? It's not used at the moment. Luckily, it doesn't break the method. For now, it would work (handle requests that is) with or without the parameter. We will use it later.

Hm, how does basic parameter validation work? What if I say /rest/books/x? Well, x is not a Long, so I will get a 404 - nice.

But if the book doesn't exist - that's a different story. In my dummy BooksBean I only have two books, so if I say /rest/books/3 the bean method returns an empty object. Not nice. When the book doesn't exist - we want to return a 404. This is why we need the response object. Let's look at the improved action method.

/**
     * Returns a single book by ID.
     *
     * @param response
     *            response
     * @param bookId
     *            book ID
     * @return single book
     * @throws IOException
     */
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public ModelAndView handleBookById(HttpServletResponse response,
            @PathVariable("id") Long bookId) throws IOException {
        ModelAndView mav = new ModelAndView();

        BooksBean booksBean = Factory.getBooksBean();

        BookTO bookTO = booksBean.loadBookById(bookId);

        if (bookTO.getId() == null) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND, "Book "
                    + bookId + " not found.");
        }
        mav.addObject(BOOK, bookTO);

        return mav;
    }

If we detect that the returned book is empty - we write to response a 404 error (SC_NOT_FOUND).

Results


That's quite a post. Let us look at the output XML and JSON.

All books in XML:

<list>
    <me.m1key.restsample.to.BookTO>
        <id>1</id>
        <name>Lord of the Rings</name>
    </me.m1key.restsample.to.BookTO>

    <me.m1key.restsample.to.BookTO>
        <id>2</id>
        <name>My Name Is Red</name>
    </me.m1key.restsample.to.BookTO>
</list>

All books in JSON (I broke it into 3 lines myself for readability):

{"books":
    [{"name":"Lord of the Rings","id":1},
    {"name":"My Name Is Red","id":2}]}

One book in XML:

<me.m1key.restsample.to.BookTO>
    <id>1</id>
    <name>Lord of the Rings</name>
</me.m1key.restsample.to.BookTO>

One book in JSON:

{"book":{"name":"Lord of the Rings","id":1}}


That's it for now. In the next post I will show you how to access this data with Dojo.

Download source code for this article

  1. REST with Spring 3.0, Spring MVC and Dojo. Part 1 - GET
  2. REST with Spring 3.0, Spring MVC and Dojo. Part 2 - GET from Dojo perspective
  3. REST with Spring 3.0, Spring MVC and Dojo. Part 3 - POST and JSR-303 validation
  4. REST with Spring 3.0, Spring MVC and Dojo. Part 4 - PUT (updating objects)
  5. REST with Spring 3.0, Spring MVC and Dojo. Part 5 - DELETE

2010-01-26

Using Scala's Specs TDD library in Eclipse

In this post I will show you a basic example of the specs library for Scala and how to run it in Eclipse.

Specs is a Behavior-Driven-Design testing framework for Scala which looks a bit different than JUnit you might be used to. However, it can be integrated with JUnit so that you may run unit tests in Eclipse as if they were JUnit unit tests. Well, almost.
This example is based on specs-1.6.1 and I used Scala 2.7.7.

For this to work you must have the specs library in your classpath as well as JUnit 4 (I used v4.5).

You must also do a bit of configuration. You must do it once per project.

1. Go to the build path configuration of your project.
2. Select the Libraries tab and hit Add Class Folder.
3. Hit Create New Folder.
4. Name it _bin (any other name should do as long as this does not exist).
5. Select Advanced and Link to folder in the file system.
6. Select the bin folder (See the screenshot below for better explanation).
7. OK.
8. Make sure that this new folder (alias) is placed above the test folder in the Order and Export tab.
9. Clean the project, rebuild.


Link to folder in the file system

And now, let's look at a very simple test.

package me.m1key.clouds

import org.specs.runner.JUnit4
import org.specs.runner._
import org.specs._

class JCloudCollectionSpecTest extends JUnit4(CloudCollectionSpec)


object CloudCollectionSpec extends Specification {
 
  "A cloud collection" should {
    "allow for adding a value and then still contain it" in {
      val cloud = new Cloud("Michal", 30)
      val collection = new CloudCollection
      collection.add(cloud)
      collection.contains(cloud) mustEqual true
    }
    // More tests
  }
}

As you can see, this line:

class JCloudCollectionSpecTest extends JUnit4(CloudCollectionSpec)

does the trick. You can now right click the project in Eclipse, select Run As -> JUnit Test and off it goes!

Update 2010-03-31: As Eric has pointed out (see his comment below the article), there is a more convenient way of writing JUnit4 compatible tests declaration, namely:

package me.m1key.clouds

import org.specs.runner.JUnit4
import org.specs.runner._
import org.specs._


class CloudCollectionSpec extends SpecificationWithJUnit {
    //...
}

So it saves you an extra declaration and makes it more succinct (note that what was an object is now a class). Thanks Eric!

Notes:
One issue is that at this moment double clicking on test reports does not work.
I used the Eclipse Scala plugin version 2.7.7.

First albums

OK, so I posted a few albums. Please take a look.

Japan 2009

Iceland 2009

Paris 2009

Assorted 2009

Barcelona 2008

Sega

Women of Amsterdam

2010-01-24

Scala and Covariance

I was recently programming a very simple Scala program that would process clouds, and by clouds I mean objects containing an item (e.g. a link) and a value.

package me.m1key.clouds

class Cloud[T](val cloudItem: T, val value: Int) {

}

As you can see, it's very simple. The cloud item will hold a URL or any other kind of object (so not just text links) and the value is an integer. I used the val keyword to make properties immutable and accessible from the outside, the Scala way that is. As you can see, this class is parametrized so that when you have a cloud with a known parametrized type you do not have to do casting (whether it would be useful in a non-academic environment is probably arguable).

I also wrote a simple collection alike class. Please see:

package me.m1key.clouds

import _root_.scala.collection.mutable.ListBuffer

class CloudCollection() {
 
  val clouds = new ListBuffer[Cloud[Any]]
 
  def add(clouds: Cloud[Any]*) = {
    for (cloud <- clouds) {
      this.clouds += cloud
    }
  }

  // Some code omitted for brevity.
 
  def getClouds(f: (Cloud[Any]) => Boolean) : Seq[Cloud[Any]] = {
    for {
      cloud <- clouds
      if f(cloud)
    } yield cloud
  }

}

Now, let's test this with the Specs library. (I will write a separate article on simple unit testing in Eclipse.)

package me.m1key.clouds

import org.specs.runner.JUnit4
import org.specs.runner._
import org.specs._

class JCloudCollectionSpecTest extends JUnit4(CloudCollectionSpec)


object CloudCollectionSpec extends Specification {
 
  "A cloud collection" should {
    "allow for adding a value and contain it" in {
      val cloud = new Cloud("Michal", 30)
      val collection = new CloudCollection
      collection.add(cloud)
      collection.contains(cloud) mustEqual true
    }
  }
  // More tests.
}

Uh-oh. That actually causes a compile-time error! What does it say?

type mismatch;
found : me.m1key.clouds.Cloud[java.lang.String]
required: me.m1key.clouds.Cloud[Any] CloudCollectionSpec.scala


Hmmm. I cannot apply Cloud[java.lang.String] to Cloud[Any] - why? Isn't every String an Any (which is roughly an equivalent of Object in Java) in Scala? Enter covariance.

Let's generalize. Imagine we have a parametrized class C (just like the Cloud class).
Now, imagine we have two instances of it, declared as C[A] (just like Cloud[String] in our example), and C[B] (Cloud[Any]).
Imagine also that A is a subclass of B (just like String is a subclass of Any). But that doesn't mean that C[A] is a subclass of C[B]! It is called invariance. Although A and B are related, C[A] and C[B] are not. That is why in our example I cannot assign Cloud[String] to Cloud[Any] - my parametrized Cloud class is invariant, i.e. it does not support covariance (nor does it support contravariance). What exactly is covariance and contravariance?

If you want the ordering to be preserved, that is, for parametrized class C:
if A is a subtype of B and you want C[A] to be a subtype of C[B], then you need covariance.
Contravariance reverses the ordering.

Scala defines so called variance annotations: plus (+) and minus (-). The plus is Scala's covariance operator. The minus - contravariance. No operator (like in the Cloud example above) means invariance.

So, when I declare my Cloud class with the covariance operator, my code compiles.

package me.m1key.clouds

class Cloud[+T](val cloudItem: T, val value: Int) {

}

Why? Because thanks to the plus operator, Cloud[java.util.String] is a subclass of Cloud[Any]. I can provide a Cloud[String] when a Cloud[Any] is expected. Or Cloud[Int]. Or Cloud[Long] etc.

One more example to clarify?

If my class is defined as C[T] then when a method expects a C[T] parameter, it can only get C[T] kind of objects. That's invariance.

If my class is defined as C[+T] then when a method expects a C[T] parameter, it can get any C[A] object provided that A is T or A is a subclass of T.

If my class is defined as C[-T] then when a method expects a C[T] parameter, it can get any C[B] object provided that B is T or B is a superclass of T.

Why is invariance the default behavior (both in Scala and in Java)? - you might ask. That is because it might cause problems when using mutable data.

One last note is: if you need more accuracy when defining e.g. method parameters, type bounds are what you are probably looking for.

Download the tiny code sample for this article.

Let's roll!

Hey guys,

I decided to start this blog on two things: programming and photography. Let's see how that goes. I hope someone will find the programming content useful or my photography interesting!

Happy Sunday,
Mikey.