Chariot Training Classes

Training Courses

I run Chariot's training and mentoring services. We provide training in AngularJS, HTML5, Spring, Hibernate, Maven, Scala, and more.

Chariot Education Services

Technology

Chariot Emerging Tech

Learn about upcoming technologies and trends from my colleagues at Chariot Solutions.

Resources

Chariot Conferences

Podcasts

Entries in REST (1)

Thursday
Jul012010

RESTing with Roo - adding Content Negotiation and REST in two easy steps (well, kinda)

Note: I don't know if I'm approaching this right... At least on Firefox, it seems to work. But on Safari, it fails. I'll revisit this soon.

Ok, here's another stream-of-consciousness post on REST support from a Roo application. I'm working on various research for our [PLUG] SpringSource Core Spring training classes, always attempting to get better examples so I can do more than just show the slides and labs, and I decided to use Roo to build myself a REST server.

Easy, I thought... Roo has RESTful URLs, and so I just have to set up the right configuration.

Kind of.

The basic steps to REST-enable a web app in Spring MVC using the built-in REST support are:

  1. Install the proper JARs for your Marshallers
  2. Install the ContentNegotiatingViewResolver

I had a bit of a hiccup figuring this out. Thanks to a great pair of threads on SpringSource's forums, here and here, I realized I had configured a few things wrong.

Setting up the example

Here's my Spring Roo log file, which you can cut and paste into a text file and then do a "roo script filename" command to set up for you.

project --topLevelPackage rest.demo --projectName spring-mvc-rest-demo
persistence setup --provider HIBERNATE --database HYPERSONIC_PERSISTENT 
entity --class ~.db.Customer
field string firstname
field string lastname
field date --fieldName dob --type java.util.Date
field number --fieldName discount --type java.math.BigDecimal --decimalMin 0 --decimalMax 1.0
controller scaffold --entity rest.demo.db.Customer --class rest.demo.web.CustomerController

Once you do that, now you're ready for some fun...

The additional dependencies for your Maven pom.xml file (you'll need to add these to the main dependencies tag section at the end...) (for XML conversion, we're using Castor, which auto-generates XML based on the reflection of JavaBean properties on your bean, and for JSON we're using the Jaskson JSON library. For this to work, you have to install spring-oxm, which isn't installed in Roo by default - I SMELL PLUGIN... RACE YA!).

<dependency>
  <groupId>org.codehaus.jackson</groupId>
  <artifactId>jackson-mapper-asl</artifactId>
  <version>1.5.3</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-oxm</artifactId>
  <version>${spring.version}</version>
</dependency>
<dependency>
  <groupId>org.codehaus.castor</groupId>
  <artifactId>castor-xml</artifactId>
  <version>1.3.1</version>
</dependency>

Now, the Spring webmvc-config.xml file changes. This file is located in src/main/webapp, under the WEB-INF/spring directory, of course. Add this to the end of the file before the closing beans tag...

<bean id="htmlMediaType" class="org.springframework.http.MediaType">
  <constructor-arg value="text" />
  <constructor-arg value="html" />
</bean>
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
  <property name="order" value="0" />
  <property name="defaultContentType">
    <ref bean="htmlMediaType" />
  </property>
  <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">
        <property name="marshaller">
          <bean class="org.springframework.oxm.castor.CastorMarshaller" />
        </property>
      </bean>
    </list>
  </property>

  <property name="viewResolvers">
    <ref bean="tilesViewResolver" />
  </property>
</bean>

Next, in the same file, add the order property, setting it to 1, to the instance of the AjaxUrlBasedViewResolver so that it looks roughly like this :

<bean class="org.springframework.js.ajax.AjaxUrlBasedViewResolver"
  id="tilesViewResolver">
  <property name="order" value="1" />
  <property name="viewClass"
    value="org.springframework.web.servlet.view.tiles2.TilesView" />
</bean>

What you're seeing at work here in this setup is the configuration of a default media type of text/html, and a configuration of a first-level view resolver that does content negotiation first. You'll see the order setting was made to make the AjaxBasedViewResolver go first, which threw me, but that's an area for more research as I start to dig deeper into the interplay between the various view resolvers... Generally you want the JSP file-based resource view resolver to go last, as it is greedy, but Tiles changes that for us a bit. I am not 100% sure everything works as advertised - for example I need to do some AJAX testing to make sure those calls work. But for getting REST against a webapp, well, there you have it.

Testing it!

Well, that wasn't so much configuration, was it? I'm experimenting with REST using the Groovy RESTClient library at the moment (I'm going to use Spring REST Template in JUnit4 for the class, but also wanted something I could script up at a moment's notice and show them how REST works without waiting for those compile cycles...) BTW, I'm on Groovy 1.7.2 (haven't yet upgraded to 1.7.3) and the @Grab line uses Groovy's GRAPE artifact manager to install a grape from the ole' vine (puts it in ~/.groovy/grape/grapes or something)... WICKED COOL. Yes, I used my blog to say WICKED COOL. You can't stop me!

@Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.5.0' )
import groovyx.net.http.RESTClient

def customers = new RESTClient( 'http://localhost:8008/demo/')
def results = customers.get (path : 'customers',
   headers: [Accept : 'application/json', "Accept-Encoding" : 'gzip,deflate'])

println results.data

Switch from JSON to XML, using application/xml as the Accept header, and you'll see it automatically respond with the appropriate datatype. Great! Now, if you use XML, change from that println statement to this:

results.data.customer.each { c ->
  println "customer:  $c.id, $c.firstname $c.lastname"
}

And if you want to test it on the browser, and see it do content negotation by file extension instead, just hit: http://localhost:8080/spring-mvc-rest-demo/customers.xml or http://localhost:8080/spring-mvc-rest-demo/customers.json to see the REST data, and http://localhost:8080/spring-mvc-rest-demo/customers to see the regular tiles views.

Enjoy...

Oh, and while I'm shilling for my training group at Chariot, please visit our course calendar if you are interested in our fall lineup - Hibernate with Spring, Spring Enterprise Integration, Core Spring, or Maven Intro and Advanced training. Thanks, and I'll refrain from using a strange chamois in any of my in-line advertisements...