2014-05-31

Looking to REST in Java? Then you’ve come to the right place, because in the blog post I will present you how to “beautifully” design a REST API and also, how to implement it in Java with the Jersey framework. The RESTful API developed in this tutorial will demonstrate a complete Create,_read,_update_and_delete (CRUD) functionality for podcast resources stored in a MySql database.

1. The example

1.1. Why?

Before we start, let me tell you why I’ve written this post – well, my intention is to offer in the future a REST API for Podcastpedia.org. Of course I could use Spring’s own REST implementation, as I currently do for the AJAX calls, but I wanted also to see how the “official” implementation looks like. So, the best way to get to know the technology is to build a prototype with it. That is what I did and what I am presenting here, and I can say that I am pretty damn satisfied with Jersey. Read along to understand why!!!

Note: You can visit my post Autocomplete search box with jQuery and Spring MVC to see how Spring handles REST requests.

1.2. What does it do?

The resource managed in this tutorial are podcasts. The REST API will allow creation, retrieval, update and deletion of such resources.

1.3. Architecture and technologies



The demo application uses a multi-layered architecture, based on the “Law of Demeter (LoD) or principle of least knowledge”[16]:

the first layer is the REST support implemented with Jersey, has the role of a facade and delegates the logic to the business layer

the business layer is where the logic happens

the data access layer is where the communcation with the pesistence storage (in our case the MySql database) takes place

A few words on the technologies/frameworks used:

1.3.1. Jersey (Facade)

The Jersey RESTful Web Services framework is open source, production quality, framework for developing RESTful Web Services in Java that provides support for JAX-RS APIs and serves as a JAX-RS (JSR 311 & JSR 339) Reference Implementation.

1.3.2. Spring (Business layer)

I like glueing stuff together with Spring, and this example makes no exception. In my opinion there’s no better way to make POJOs with different functionalities. You’ll find out in the tutorial what it takes to integrate Jersey 2 with Spring.

1.3.3. JPA 2 / Hibernate (Persistence layer)

For the persistence layer I still use a DAO pattern, even though for implementing it I am using JPA 2, which, as some people say, should make DAOs superfluous (I, for one, don’t like my service classes cluttered with EntityManager/JPA specific code). AS supporting framework for JPA 2 I am using Hibernate.

See my post Java Persistence Example with Spring, JPA2 and Hibernate for an interesting discussion around persistence thema in Java.

1.3.4. Web Container

Everything gets packaged with Maven as a .war file and can be deployed on any web container – I used Tomcat and Jetty but, it could also be Glassfih, Weblogic, JBoss or WebSphere.

1.3.5. MySQL

The sample data is stored in a MySQL table:



1.3.6. Technology versions

Jersey 2.9

Spring 4.0.3

Hibernate 4

Maven 3

Tomcat 7

Jetty 9

MySql 5.6

Note: The main focus in the post will be on the REST api design and its implementation with the Jersey JAX-RS implementation, all the other technologies/layers are considered as enablers.

1.4. Source code

The source code for the project presented here is available on GitHub, with complete instructions on how to install and run the project:

Codingpedia / demo-rest-jersey-spring

2. Configuration

Before I start presenting the design and implementation of the REST API, we need to do a little configuration so that all these wonderful technologies can come and play together

2.1. Project dependencies

The Jersey Spring extension must be present in your project’s classpath. If you are using Maven add it to the pom.xml file of your project:

Note: The jersey-spring3.jar, uses its own version for Spring libraries, so to use the ones you want (Spring 4.0.3.Release in this case), you need to exclude these libraries manually.

Code alert: If you want to see what other dependencies are needed (e.g. Spring, Hibernate, Jetty maven plugin, testing etc.) in the project you can have a look at the the complete pom.xml file available on GitHub.

2.2. web.xml

2.2.1. Jersey-servlet

Notice the Jersey servlet configuration [lines 18-33]. The javax.ws.rs.core.Application class defines the components (root resource and provider classes,) of the JAX-RS application. I used ResourceConfig, which is Jersey’s own implementation of the class Application, and which provides advanced capabilites to simplify registration of JAX-RS components. Check out the JAX-RS Application Model in the documentation for more possibilities.

My implementation of the ResourceConfig class, org.codingpedia.demo.rest.RestDemoJaxRsApplication, registers application resources, filters, exception mappers and feature :

Please note the

org.glassfish.jersey.server.spring.scope.RequestContextFilter, which is a Spring filter that provides a bridge between JAX-RS and Spring request attributes

org.codingpedia.demo.rest.resource.PodcastsResource, which is the “facade” component that exposes the REST API via annotations and will be thouroughly presented later in the post

org.glassfish.jersey.jackson.JacksonFeature, which is a feature that registers Jackson JSON providers – you need it for the application to understand JSON data

2.1.2.2. Spring application context configuration

The Spring application context configuration is located in the classpath under spring/applicationContext.xml:

Nothing special here, it just defines the beans that are needed throughout the demo application (e.g. podcastsResource which is the entry point class for our REST API).

3. The REST API (design & implementation)

3.1. Resources

3.1.1. Design

As mentioned earlier, the demo application manages podcasts, which represent the resource in our REST API. Resources are the central concept in REST and are characterized by two main things:

each is referenced with a global identifier (e.g. a URI in HTTP).

has one or more representations, that they expose to the outer world and can be manipulated with (we’ll be working mostly with JSON representations in this example)

Resources are usually represented in REST by nouns (podcasts, customers, user, accounts etc.) and not verbs (getPodcast, deleteUser etc.)

The endpoints used throughout the tutorial are :

/podcasts – (notice the plural) URI identifying a resource representing a collection of podcasts

/podcasts/{id} – URI identifying a podcast resource, by the podcast’s id

3.1.2. Implementation

For the sake of simplicity, a podcast will have only the following properties:

id – uniquely identifies the podcast

feed – url feed of the podcast

title – title of the podcast

linkOnPodcastpedia – where you can find the podcast on Podcastpedia.org

description – a short description of the podcast

I could have used only one Java class for the representation of the podcast resource in the code, but in that case the class and its properties/methods would have gotten cluttered with both JPA and XML/JAXB/JSON annotations. I wanted to avoid that and I used two representations which have pretty much the same properties instead:

PodcastEntity.java – JPA annotated class used in the DB and business layers

Podcast.java – JAXB/JSON annotated class used in the facade and business layers

Note: I am still trying to convince myself that this is the better approach, so if you have a suggestion on this please leave a comment.

The Podcast.java classs look something like the following:

and translates into the following JSON representation, which is actually the de facto media type used with REST nowadays:

Even though JSON is becoming more and more the preffered representation in REST APIs, you shouldn’t neglect the XML representation, as most of the systems still use XML format for communication with other parties.

The good thing is that in Jersey you can kill two rabbits with one shot – with JAXB beans (as used above) you will be able to use the same Java model to generate JSON as well as XML representations. Another advantage is simplicity of working with such a model and availability of the API in Java SE Platform.

Note: Most of the methods defined in this tutorial will produce and consume also the application/xml media type, with application/json being the preferred way.

3.2. Methods

Before I present you the API, let me to tell you that

Create = POST

Read = GET

Update = PUT

Delete = DELETE

and is not a strict 1:1 mapping. Why? Because you can also use PUT for Creation and POST for Update. This will be explained and demonstrated in the coming paragraphs.

Note: For Read and Delete it is pretty clear, they map indeed one to one with the GET and DELETE HTTP operations. Anyway REST is an architectural style, is not a specification and you should adapt the architecture to your needs, but if you want to make your API public and have somebody willing to use it, you should follow some “best practices”.

As already mentioned the PodcastRestResource class is the one handling all the rest requests:

Notice the @Path("/podcasts") before the class definition – everything related to podcast resources will occur under this path. The @Path annotation’s value is a relative URI path. In the example above, the Java class will be hosted at the URI path /podcasts. The PodcastService interface exposes the business logic to the REST facade layer.

Code alert: You can find the entire content of the class on GitHub – PodcastResource.java. We’ll be going through the file step by step and explain the different methods corresponding to the different operations.

3.2.1. Create podcast(s)

3.2.1.1. Design

While the “most known” way for resource creation is by using POST, As mentioned before to create a new resource I could use both the POST and PUT methods, and I did just that:

  Description

  URI

  HTTP method

  HTTP Status response

 Add new podcast

 /podcasts/

POST

201 Created

 Add new podcast (all values must be sent)

 /podcasts/{id}

PUT

201 Created

The big difference between using POST (not idempotent)

“The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line[...] If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity which describes the status of the request and refers to the new resource, and a Location header” [1]

and PUT (idempotent)

“The PUT method requests that the enclosed entity be stored under the supplied Request-URI [...] If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI. If a new resource is created, the origin server MUST inform the user agent via the 201 (Created) response.” [1]

, is that for PUT you should know beforehand the location where the resource will be created and send all the possible values of the entry.

3.2.1.2. Implementation

3.2.1.2.1. Create a single resource with POST

Annotations

@POST – indicates that the method responds to HTTP POST requests

@Consumes({MediaType.APPLICATION_JSON}) – defines the media type, the method accepts, in this case "application/json"

@Produces({MediaType.TEXT_HTML}) – defines the media type) that the method can produce, in this case "text/html".

Response

on success: text/html document, with a HTTP status of 201 Created, and a Location header specifying where the resource has been created

on error:

400 Bad request if not enough data is provided

409 Conflict if on the server side is determined a podcast with the same feed exists

3.2.1.2.2. Create a single resource (“podcast”) with PUT

This will be treated in the Update Podcast section below.

3.2.1.2.3. Bonus – Create a single resource (“podcast”) from form

Annotations

@POST – indicates that the method responds to HTTP POST requests

@Consumes({MediaType.APPLICATION_FORM_URLENCODED})- defines the media type, the method accepts, in this case"application/x-www-form-urlencoded"

@FormParam – present before the input parameters of the method, this annotation binds the value(s) of a form parameter contained within a request entity body to a resource method parameter. Values are URL decoded unless this is disabled using the Encoded annotation

@Produces({MediaType.TEXT_HTML}) – defines the media type that the method can produce, in this case “text/html”. The response will be a html document, with a status of 201, indicating to the caller that the request has been fulfilled and resulted in a new resource being created.

Response

on success: text/html document, with a HTTP status of 201 Created, and a Location header specifying where the resource has been created

on error:

400 Bad request if not enough data is provided

409 Conflict if on the server side is determined a podcast with the same feed exists

3.2.2. Read podcast(s)

3.2.2.1. Design

The API supports two Read operations:

return a collection of podcasts

return a podcast identified by id

 Description

 URI

 HTTP method

 HTTP Status response

 Return all podcasts

 /podcasts/?orderByInsertionDate={ASC|DESC}&numberDaysToLookBack={val}

GET

200 OK

 Add new podcast (all values must be sent)

 /podcasts/{id}

GET

200 OK

Notice the query parameters for the collection resource - orderByInsertionDate and numberDaysToLookBack. It makes perfect sense to add filters as query parameters in the URI and not be part of the path.

3.2.2.2. Implementation

3.2.2.2.1. Read all podcasts (“/”)

Annotations

@GET - indicates that the method responds to HTTP GET requests

@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) – defines the media type) that the method can produce, in this case either "application/json" or "application/xml"(you need the @XmlRootElement in front of the Podcast class ). The response will be a list of podcasts either in JSON or XML format.

Response

list of podcasts from the database and a HTTP Status of 200 OK

3.2.2.2.1. Read one podcast

Annotations

@GET – indicates that the method responds to HTTP GET requests

@Path("{id}") – identifies the URI path that the class method will serve requests for. The “id” value is an embedded variable making an URI path template. It is used in combination with the @PathParam variable.

@PathParam("id") – binds the value of a URI template parameter (“id”) to the resource method parameter. The value is URL decoded unless this is di sabled using the @Encoded annotation. A default value can be specified using the @DefaultValue annotation.

@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) – defines the media type) that the method can produce, in this case "application/json" or "application/xml"(you need the @XmlRootElement in front of the Podcast class ).

Response

on success: requested podcast with a 200 OK HTTP status. The format is either xml or JSON, depending on the Accept -header’s value sent by the client (might bet application/xml or application/json)

on error: 404 Not found if the podcast with the given id does not exist in the database

3.2.3. Update podcast

3.2.3.1. Design

Description

URI

HTTP method

HTTP Status response

 Update podcast (fully)

 /podcasts/{id}

PUT

200 OK

 Update podcast (partially)

 /podcasts/{id}

POST

200 OK

In the REST arena you will be doing two kind of updates:

full updates – that is where you will provide all the

partial updates – when only some properties will be sent over the wire for update

For full updates, it’s pretty clear you can use the PUT method and you are conform the method’s specification in the RFC 2616.

Now for the partial update there’s a bunch of proposals/debate on what to use:

via PUT

via POST

via PATCH

Let me tell why I consider the first option (with PUT) is a NO GO. Well, accordingly to the specification

“If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server.“[1]

if I would like to update just the title property of the podcast with the id 2

then, accordingly to the specification the resource “stored” at the location should have only id and title, and clearly my intent was not that.

The second option via POST… well we can “abuse” this one and that is exactly what I did in the implementation, but it does not seem conform to me, because the spec for POST states:

“The posted entity is subordinate to that URI in the same way that a file is subordinate to a directory containing it, a news article is subordinate to a newsgroup to which it is posted, or a record is subordinate to a database.“[1]

That does not look like a partial update case to me…

The third option is to use PATCH,  and I guess this is the main reason the method came to life:

“Several applications extending the Hypertext Transfer Protocol (HTTP)
   require a feature to do partial resource modification.  The existing
   HTTP PUT method only allows a complete replacement of a document.
   This proposal adds a new HTTP method, PATCH, to modify an existing
   HTTP resource.”[2]

I am pretty sure this will be used in the future for partial updates, but since is not yet part of the specification and not yet implemented in Jersey I chose to use the second option with POST for this demo. If you really want to implement partial update in Java with the PATCH check out this post  - Transparent PATCH support in JAX-RS 2.0

3.2.3.1. Implementation

3.2.3.1.1. Full Update

Annotations

@PUT - indicates that the method responds to HTTP PUT requests

@Path("{id}") – identifies the URI path that the class method will serve requests for. The “id” value is an embedded variable making an URI path template. It is used in combination with the @PathParam variable.

@PathParam("id") – binds the value of a URI template parameter (“id”) to the resource method parameter. The value is URL decoded unless this is di sabled using the @Encoded annotation. A default value can be specified using the @DefaultValue annotation.

@Consumes({MediaType.APPLICATION_JSON}) – defines the media type, the method accepts, in this case "application/json"

@Produces({MediaType.TEXT_HTML}) – defines the media type) that the method can produce, in this case “text/html”.

will be a html document containing different messages and stati depending on what action has been taken

Respsonse

on creation

on success: 201 Created and in the Location header the specified location where the resource was created

on error: 400 Bad request if the minimum required properties are not provided for insertion

on full update

on success: 200 OK

on error: 400 Bad Request if not all properties are provided

3.2.3.1.2. Partial Update

Annotations

@POST – indicates that the method responds to HTTP POST requests

@Path("{id}") – identifies the URI path that the class method will serve requests for. The “id” value is an embedded variable making an URI path template. It is used in combination with the @PathParam variable.

@PathParam("id") – binds the value of a URI template parameter (“id”) to the resource method parameter. The value is URL decoded unless this is di sabled using the @Encoded annotation. A default value can be specified using the @DefaultValue annotation.

@Consumes({MediaType.APPLICATION_JSON}) – defines the media type, the method accepts, in this case "application/json"

@Produces({MediaType.TEXT_HTML}) – defines the media type) that the method can produce, in this case "text/html".

Response

on success: 200 OK

on error: 404 Not Found, if there is no resource anymore available at the provided location

3.2.4. Delete podcast

3.2.4.1. Design

Description

URI

HTTP method

HTTP Status response

 Removes all podcasts

 /podcasts/

DELETE

204 No content

 Removes podcast at the specified location

 /podcasts/{id}

DELETE

204 No content

3.2.4.2. Implementation

3.2.4.2.1. Delete all resources

Annotations

@DELETE – indicates that the method responds to HTTP DELETE requests

@Produces({MediaType.TEXT_HTML}) – defines the media type that the method can produce, in this case “text/html”.

Response

The response will be a html document, with a status of 204 No content, indicating to the caller that the request has been fulfilled.

3.2.4.2.2. Delete one resource

Annotations

@DELETE – indicates that the method responds to HTTP DELETE requests

@Path("{id}") – identifies the URI path that the class method will serve requests for. The “id” value is an embedded variable making an URI path template. It is used in combination with the @PathParam variable.

@PathParam("id") – binds the value of a URI template parameter (“id”) to the resource method parameter. The value is URL decoded unless this is di sabled using the @Encoded annotation. A default value can be specified using the @DefaultValue annotation.

@Produces({MediaType.TEXT_HTML}) – defines the media type that the method can produce, in this case “text/html”.

Response

on success: if the podcast is removed a 204 No Content success status is returned

on error: podcast is not available anymore and status of 404 Not found is returned

4. Logging

Every request’s path and the response’s entity will be logged when the logging level is set to DEBUG. It is developed like a wrapper, AOP-style functionality with the help of Jetty filters.

See my post How to log in Spring with SLF4J and Logback for more details on the matter.

5. Exception handling

In case of errros, I decided to response with unified error message structure.  Here’s an example how an error response might look like:

Note: Stay tuned, because the following post will present more details about error handling in REST with Jersey.

6. Add CORS support on the server side

I extended the capabilities of the API developed for the tutorial to support Cross-Origing Resource Sharing (CORS) on the server side.

Please see my post How to add CORS support on the server side in Java with Jersey for more details on the matter.

7. Testing

7.1. Integration tests in Java

To test the application I will use the Jersey Client and execute requests against a running Jetty server with the application deployed on it. For that I will use the Maven Failsafe Plugin.

7.1.1. Configuration

7.1.1.1 Jersey client dependency

To build a Jersey client the jersey-client jar is required in the classpath. With Maven you can add it as a dependency to the pom.xml file:

7.1.1.2. Failsafe plugin

The Failsafe Plugin is used during the integration-test and verify phases of the build lifecycle to execute the integration tests of the application. The Failsafe Plugin will not fail the build during the integration-test phase thus enabling the post-integration-test phase to execute.

To use the Failsafe Plugin, you need to add the following configuration to your pom.xml

7.1.1.2. Jetty Maven Plugin

The integration tests will be executed against a running jetty server, that will be started only for the execution of the tests. For that you have to configure the following executionin the jetty-maven-plugin:

Note: In the pre-integration-test phase the Jetty server will be started, after stopping any running instance to free up the port, and in the post-integration-phase it will be stopped. The scanIntervalSeconds has to be set to 0, and daemon to true.

Code alert: Find the complete pom.xml file on GitHub

7.1.2. Build the integration tests

I am using JUnit as the testing framework. By default, the Failsafe Plugin will automatically include all test classes with the following wildcard patterns:

"**/IT*.java" – includes all of its subdirectories and all java filenames that start with “IT”.

"**/*IT.java" – includes all of its subdirectories and all java filenames that end with “IT”.

"**/*ITCase.java" – includes all of its subdirectories and all java filenames that end with “ITCase”.

I have created a single test class – RestDemoServiceIT – that will test the read (GET) methods, but the procedure should be the same for all the other:

Note:

I had to register the JacksonFeature for the client too so that I can marshall the podcast response in JSON format – response.readEntity(Podcast.class)

I am testing against a running Jetty on port 8888 – I will show you in the next section how to start Jetty on a desired port

I am expecting a 200 status for my request

With the help org.codehaus.jackson.map.ObjectMapper I am displaying the JSON response pretty formatted

7.1.3. Running the integration tests

The Failsafe Plugin can be invoked by calling the verify phase of the build lifecycle.

To start jetty on port 8888 you need to set the jetty.port property to 8888. In Eclipse I use the following configuration:



Run integration tests from Eclipse

7.2. Integration tests with SoapUI

Recently I’ve rediscovered SoapUI after using it heavily for testing SOAP based web services. With the recent versions (at the time of writing latest is 5.0.0) it offers pretty good functionality to test REST based web services, and coming versions should improve on this. So unless you develop your own framework/infrastructure to test REST services, why not give it a try to SoapUI. I did, I was satisfied with the results so far and I’ve decided to do a video tutorial, that you can now find on YouTube on our channel:

8. Versioning

There are three major possibilities

URL:  “/v1/podcasts/{id}”

Accept/Content-type header: application/json; version=1

Because I am a developer and not a RESTafarian yet I would do the URL option. All I would have to do on the implementation side for this example, would be to modify the @Path‘s value annotation on the PodcastResource class from to

Of course on a production application, you wouldn’t want every resource class preprefixed with the version number,  you’d want the version somehow treated through a filter in a AOP manner. Maybe something like this will come in a following post…

Here are some great resources from people that understand better the matter:

[Video] REST+JSON API Design – Best Practices for Developers

Your API versioning is wrong, which is why I decided

Show more