JBoss Development is brought to you in partnership with:

I'm Solomon Duskis, NYC consultant and a Java/J2EE guy. I work at Sungard Consulting Services in NYC. The postings on this site are my own and do not necessarily represent the positions, strategies or opinions of my employer. Solomon is a DZone MVB and is not an employee of DZone and has posted 22 posts at DZone. You can read more from them at their website. View Full User Profile

Integrating JBoss RESTEasy and Spring MVC

10.15.2009
| 101492 views |
  • submit to reddit

ContactsResource.java

MVC Controllers control the flow between the Model and the View. Resource is REST's equivalent to Controllers, and we'll be using the term Resource and Controller interchangably. In our case, our resource handles requests to /contracts and /contracts/{id}. Our ContractsResource must perform quite a few functions on those two URL templates:

  • Retrieve all Contacts - Display the results in either HTML, XML or JSon. For clarity, we'll break out the data oriented functionality (XML and JSon) from the user oriented functionality (HTML) into two distinct URLs - /contacts for HTML and /contacts/data for XML and JSon. REST allows a client to select which format it prefers to receive the data in through a process called Content Negotiation. Content Negotiation can happen through HTTP headers, URI or query parameters. Our ContractsResource will use different URIs to differentiate between data oriented and user views, and will use HTTP headers to differentiate between XML and JSon data views.
  • Save a Contact - Create or Updating data is a pretty standard requirement. The Save a Contact functionality mirrors the Content Negotiation needs of Retrieve all Contacts. User oriented data exchange comes in the form of HTML form data, and data oriented exchange usually occurs in XML and JSon. These differing requirements require ContractResource to have two distinct JAX-RS Java methods; we'll also separate the URLs for clarity purposes.
  • View a Contact - We'll create a single view for viewing a single contact that returns XML or JSon. We leave the user oriented view as an exercise for the reader.

Here's another view of our requirements:

FunctionalityURLFormatJava Method
User Oriented View All/contactsHTMLviewAll()
Data Oriented View All/contacts/dataXML or JSongetAll()
User Oriented Save/contacts/Form datasaveContactForm()
Data Oriented Save/contacts/dataXML or JSonsaveContact()
Data Oriented View Single/contacts/data/{lastName}XML or JSonget()

Note that we mixed and matched HTML and data oriented functionality in this requirement. Now that we have our requirements in place, let's take a look at the ContactsResource code. There are quite a few new Spring and JAX-RS annotations which we'll explain right after the code:

@Controller
@Path(ContactsResource.CONTACTS_URL)
public class ContactsResource
{
public static final String CONTACTS_URL = "/contacts";
@Autowired
ContactService service;

@GET
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Path("data")
public Contacts getAll() {
return service.getAll();
}

@GET
@Produces(MediaType.TEXT_HTML)
public ModelAndView viewAll() {
// forward to the "contacts" view, with a request attribute named
// "contacts" that has all of the existing contacts
return new ModelAndView("contacts", "contacts", service.getAll());
}

@PUT
@POST
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Path("data")
public Response saveContact(@Context UriInfo uri, Contact contact)
throws URISyntaxException {
service.save(contact);
URI newURI = UriBuilder.fromUri(uri.getPath()).path(contact.getLastName()).build();
return Response.created(newURI).build();
}

@POST
@PUT
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.TEXT_HTML)
public ModelAndView saveContactForm(@Form Contact contact)
throws URISyntaxException {
service.save(contact);
return viewAll();
}

@GET
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Path("data/{lastName}")
public Contact get(@PathParam("lastName") String lastName) {
return service.getContact(lastName);
}
}

This code is packed with annotations and Java code that's indicative of JAX-RS Resources and Spring applications. There is also a RESTEasy custom annotation.

The Spring IoC annotations are well documented, but we are using them in unusual ways for our integration:
  • @Controller tells the Spring runtime that it needs to create an instance of ContractsResource at startup time. Do you remember the component-scan directive that was used in the Spring configuration section? The combination of the directive and the annotation tell Spring that a singleton instance of ContractsResource must be created at startup. Spring has a more generic @Component, but the use of @Controller allows for more precise definition of bean usage and also allows for future upgrades that involve AoP to create more precise targeting. While @Controller is usually associated with Spring @MVC annotated controllers and not other Controller infrastructures, but even thought it's not a Spring MVC controller, we use it to tell Spring that this indeed is a Controller That association of @Controller to Spring MVC annotated controllers is a loose coupling in the Spring runtime. We'll use JAX-RS annotations to configure the handling of URL and HTTP handling behavior. You could theoretically add additional Spring @MVC annotations such as @RequestMapping (which is an equivalent of JAX-RS @Path) to our ContactsResource, if you really wanted to
  • @Autowired tells the Spring runtime that instances of ContractResource require an instance of ContactService. We'll be coding the ContactService later in this article. You can take a look at the Spring reference documentation for more information about @Autowired and @Controller.

  • The last Spring artifact that we use is ModelAndView: It is Spring MVC's encapsulation of which logical View to use and which Model variables should be passed into the View. In our case, we're going to create a Model variable called "contacts" that is a List of all Contact objects we have in the system. We're passing that variable to the a logical view named "contacts" which will map to "/WEB-INF/contacts.jsp" based on the Spring configuration that we previously discussed.

The JAX-RS annotations are also well documented, but it's definitely worth while to give a brief overview:
  • @Path tells the RESTEasy (or other JAX-RS environments) how to map URLs to java methods. Adding @Path at the class level tells, in our case "/contacts", indicates that all methods must be prefixed with that URL. The @Path value can either be a hard coded URL such as "/contacts" or it can be a URI template such as "data/{lastName}". You can even specify regular expressions for more sophisticated filtering in the URI template.

  • @GET, @PUT and @POST are used in combination with @Path to indicate which specific HTTP methods are handled by individual Java methods

  • @Produces and @Consumes are used to further filter how a request should be handled based on content negotiation based on the Accept and Content_Type HTTP header. JAX-RS provides a set of default mime type values in the MediaType class.

  • @PathParam is a method parameter annotation that indicates how a URI template variable is mapped to a method parameter. There are quite a few other method parameter level annotations that you could use to map HTTP headers, cookies, query parameters and form parameters to member variables
  • @Context is an interesting JAX-RS parameter that allows dependency injection of request level information such as HttpRequest, HttpResponse and UriInfo (which as you can probably guess encapsuldates information about the request URI). It's important to note that Spring by default manages beans such as ContactsResource as a singleton; if ContactsResource was a Prototype or Request scoped bean, you would be able to use the @Context annotation on member variables in addition to method variables. For more on Spring scoping see the Spring Framework documentation.

The last annotation we need to talk about is @Form. It's a RESTEasy custom annotation that describes that a member variable encapsulates data from HTML forms. If you recall, we used the JAX-RS @FormParam annotation on our Contact domain object. @Form and @FormParam are used in concert to allow for better maintenance of form based processing systems. JAX-RS 2.0's stated goals include a more robust, uniform Form processing annotation system.
The functionality to code ratio is pretty high because of all of the declarative coding conventions of these annotations.
Now that we've discussed the most involved pieces of the puzzle, let's take a look at completing the project.

Additional Artifacts

pom.xml

Our pom.xml includes dependency management, description of required Maven repositories, a description of which JDK we're going to use and a Jetty web server configuration. We'll cover the repository selection, the dependencies specific to RESTEasy and a jetty-maven integration

External Repositories

    <repositories>
<repository>
<id>jboss</id>
<name>jboss repo</name>
<url>http://repository.jboss.org/maven2</url>
</repository>
<repository>
<id>scannotation</id>
<url>http://scannotation.sf.net/maven2</url>
</repository>
<repository>
<id>java.net</id>
<url>http://download.java.net/maven/1</url>
<layout>legacy</layout>
</repository>
<repository>
<id>maven repo</id>
<name>maven repo</name>
<url>http://repo1.maven.org/maven2/</url>
</repository>
</repositories>

Project Dependencies

Now that we've informed Maven which additional repositories are required, we can now include the dependencies the our sample project will require. The <dependencies/> section of the pom.xml file, should include the following two dependencies for Spring and RESTEasy functionality - resteasy-spring and resteasy-jaxb-provider:
    <dependencies>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-spring</artifactId>
<version>1.2.RC1</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>1.2.RC1</version>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.15</version>
<scope>test</scope>
</dependency>
<!-- Other dependencies ... -->
</dependencies>

The resteasy-spring dependency includes the adapter that integrates RESTEasy into Spring's MVC and provides most the required Java dependencies for RESTEasy and Spring. It also contains Spring configuration needed within the embedded spring-resteasy.xml file that will be used in the Spring configuration section. The other RESTEasy dependency that's included, resteasy-jaxb-provider, contains classes that convert the payload into various formats before sending it to the client.

The last dependency to focus on is the maven-jetty-plugin which allows us to easily startup our project in a Jetty webserver environment.

Note: If you're follow the link above to the RESTEasy repository's version of pom.xml, you will have to modify the version of resteasy-spring and resteasy-jaxb-provider to the latest version that has been deployed, specifically 1.2.RC1 at the time this article was written. The RESTEasy repository contains a soon-to-be-deployed version number which will not work unless you build the entire RESTEasy project.

 

Maven Jetty Plugin

One last interesting item of pom.xml is the configuration of the Jetty web server
  <build>
<finalName>resteasy-springMVC</finalName>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.15</version>
<configuration>
<contextPath>/</contextPath>
<scanIntervalSeconds>2</scanIntervalSeconds>
</configuration>
</plugin>
...
</plugins>
</build>

This will allow us to startup Jetty against localhost:8080. You can learn more about the maven Jetty plugin and a variety of configuration options.
Let's start with the domain model and move on to the service object. From there, we'll discuss the JAX-RS Resource/Controller. From there, we'll explore the unit test and finally we'll write the JSP View and start up our server.

Contact.java

Our DTO is going to be deceptively simple. It will perform a dual responsiblity of JAXB XML binding and Form parameter binding. Both sets of functionality will be configured through annotations and will be managed through JAXB and JAX-RS:
import javax.ws.rs.FormParam;
import javax.xml.bind.annotation.XmlRootElement;

@XMLRootElement

public class Contact {
private String firstName, lastName;

// default constructor for JAXB (also required by JPA/Hibernate if you use them)
public Contact(){}

// helper constructor for our Controller/Service operations
public Contact(String firstName, String lastName){
this.firstName = firstName;
this.lastName = lastName;
}

@FormParam("firstName")
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getFirstName() { return firstName; }

@FormParam("lastName")

public void setLastName(String lastName) { this.lastName = lastName; }
public String getLastName() { return lastName; }

// equals and hasCode are added for the Map based Service object

public boolean equals(Object other){ .. }
public long hashCode(){ .. }
}

 

The annotation on the setters tells JAX-RS to bind any incoming form parameters to the appropriate setter. The @XMLRootElement annotation is enough to tell JAXB that the Contract object must be bound to getters and setters must be bound to an XML document that will look like:

 

<contact>
<firstName>Richard</firstName>
<lastName>Burton</lastName>
</contact>

Contacts.java

The Contacts class is a simple wrapper around a List of Contact instances:


@XmlRootElement
public class Contacts {
private Collection<Contact> contacts;

public Contacts() {
this.contacts = new ArrayList<Contact>();
}

public Contacts(Collection<Contact> contacts) {
this.contacts = contacts;
}

@XmlElement(name="contact")
public Collection<Contact> getContacts() { return contacts; }
public void setContacts(Collection<Contact> contact){ this.contacts = contact; }
}
Contacts has the @XmlRootElement, just like Contact. The @XmlRootElement annotation tells JAXB to transform objects of this type to an XML structure that has <contacts> as its top level element. In addition, we've added the @XmlElement annotation to the getContacts() method. By default, JAXB renders all JavaBean elements and uses the JavaBean name as the element. JAXB handles Lists as special cases: all List elements are translated to XML elements using the JavaBean name. @XmlElement(name="contact") tells JAXB that we opted to override the default name ("contracts") in favor of our own name ("contract" - no 's').
The Contracts object will bind to XML that looks like:
<contracts>
<contract>
<firstName>Richard<firstName>
<lastName>Burton</lastName>
</contract>
<contract>
<firstName>Solomon<firstName>
<lastName>Duskis</lastName>
</contract>
</contracts>

Now that we have our Domain model in place, let's start using it in our Service tier.

ContactService.java

Since the purpose of this article is JAX-RS centric, we're not going to create an elaborate service layer, but we'll add once since creating more robust Spring applications do require service or data access layers. If you're interested in seeing a RESTEasy/Spring application with database access, look here. Our ContractService performs simple in-memory storage of Contacts by last name:
@Service
public class ContactService
{
private Map<String, Contact> contactMap = new ConcurrentHashMap<String, Contact>();

public void save(Contact contact){ contactMap.put(contact.getLastName(), contact); }
public Contact getContact(String lastName){ return contactMap.get(lastName); }
public Contacts getAll() { return new Contacts(contactMap.values()); }
}
There are two items of interest that are noteworthy:
  • Notice the use of Spring's @Service annotation. Do you remember the component-scan directive that was used in the Spring configuration section? The combination of the directive and the annotation tell Spring that a singleton instance of ContractService must be created at startup. Spring has a more generic @Component, but the use of @Service allows for more precise definition of bean usage and also allows for future upgrades that involve AoP to create more precise targeting.
  • Notice the use of ConcurrentHashMap. It's a JDK 1.5 addition that adds performance in multi-threaded environments. It's an easy way to boost performance in distributed REST applications

Next, let's take a look at the JSP that

contacts.jsp

We've explored the Model and Controller aspects of MVC. The last piece to the puzzle is the View. Most JAX-RS based interactions perform a more automated conversion of objects like our Contact to a data-oriented view, such as XML or JSon. Traditionally, Java EE MVC has been done with a more manual View management with languages such as JSP. Our JSP will take a Contracts instance created in ContractsResources.viewAll() and render it in basic HTML:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<body>
<h2>Hello Contacts!</h2>
<div id="contact-data" class="contacts">
<c:forEach var="contact" items="${contacts.contacts}">
<span class="contact">Hello <a href="/contacts/data/${contact.lastName}">${contact.firstName} ${contact.lastName}</a></span>
</c:forEach>
</div>

<p>Save a contact, save the world:</p>
<form action="/contacts" method="post">
First Name: <input type="text" name="firstName" /><br>
Last Name: <input type="text" name="lastName" /><br>
<input type="submit" value="submit"/>
</form>
</body>
</html>

This JSP loops over all contacts and adds links to their data-oriented View. It also creates a simple HTML form for creating a new Contact. While this JSP is simple, it will help us exercise three of our ContactsResource Controller: viewAll(), .saveContactForm(), and get(). It could also be a spring board for more complicated AJAX/JSon interaction, but that's beyond the scope of this article.

The code and configuration is now complete, so let's run this project!


Published at DZone with permission of Solomon Duskis, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

christophe charles replied on Fri, 2009/10/23 - 6:52am

Thanks !

Seb Cha replied on Wed, 2009/11/04 - 12:16pm

Good JAX-RS tutorial !

Tony Childs replied on Sat, 2010/05/01 - 9:58am

Thanks for the great article. There is something I have not been able to figure out, however.

Without Spring integration the RestEasy ConfigurationBootstrap.createDeployment() method adds any RestEasy context params - more specifically "resteasy.media.type.mappings" to map type extensions to MIME types.

Unfortunately, when Spring is integrated, these mappings are not picked up. I have been stepping through the code trying to figure out how I might be able to pass these context params to RestEasy when I noticed you had authored some classes in the Resteasy-Spring integration package, so I thought you might be the best person to ask. What would be the best way to get these mappings to RestEasy?

Thanks, Tony

Tony Childs replied on Sat, 2010/05/01 - 11:02am

After further examination of the startup behavior I managed to figure out a way to get the mappings in there. I copied your springmvc-resteasy.cml file and used it as a template for my own custom file. I then added/changed the folloing lines:

<bean id="jsonType" class="javax.ws.rs.core.MediaType" factory-method="valueOf">
   <constructor-arg value="application/json"/>
</bean>

<bean id="xmlType" class="javax.ws.rs.core.MediaType" factory-method="valueOf">
   <constructor-arg value="application/xml"/>
</bean>

<bean id="resteasy.dispatcher" class="org.jboss.resteasy.core.SynchronousDispatcher"
      depends-on="resteasy.intializer">
   <constructor-arg ref="resteasy.providerFactory"/>
   <property name="mediaTypeMappings">
      <map>
         <entry key="json" value-ref="jsonType"/>
         <entry key="xml" value-ref="xmlType"/>
      </map>
   </property>
</bean>

Suresh Bhaskaran replied on Fri, 2010/08/27 - 7:53pm in response to: christophe charles

Great Article! If any of you run into the following issue: "No mapping found for HTTP request with URI [/main/blog/list] in DispatcherServlet with name 'spring' ERROR org.jboss.resteasy.springmvc.ResteasyHandlerMapping - Resource Not Found: Could not find resource for relative Then all you have to do is modify your spring app context xml file: Add these 2 lines: (I am omitting the cone opening and closing cone brackets, as they do not appear here for some reasons: context:component-scan base-package="com.zyrisinc.googlecheckout.service" bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" property name="order" value="0" bean bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"

Pasta Farian replied on Wed, 2010/09/22 - 2:44pm

Update: I can access it by going to http://localhost:8080/examples-resteasy-springMVC-2.1-SNAPSHOT/contacts but when I try to add a contact, I end up back on http://localhost:8080/contacts which gives me a 404 and apparently no contacts were added. By the way, with Tomcat I needed to add jstl.jar to the runtime. ----- Hi, Great article. Very helpful. I am having trouble running this WAR on Tomcar 6. The WAR deploys without any errors but when I go to http://localhost:8080/contacts I get a HTTP 404. Can anyone deploy this on Tomcat successfully? or do I need to make some changes to the servlet mapping? Thanks.

Clay Press replied on Sun, 2011/10/16 - 11:07pm

Two Years After, this jboss/resteasy/spring/maven tutorial still builds and tests as advertised.  Nice demo.

Will java rest keep pace with the dynamic type competitive alternatives in the POST-sun.com era?  Which decision trumps:  resteasy vs jersey vs spring vs restlet <or> java/c@ vs python/ruby.

Two Years Later, where will java rest be

 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.