Monday, November 25, 2013

RESTful services with jQuery and Java using JAX-RS and Jersey

NOTE: This is the Java version of this article and its companion app. A PHP version is availablehere.
This is a more in depth version of my previous post on the same topic. The previous article only covered the HTTP GET method for building RESTful services. This article (and its new companion app) provides an example of building a complete RESTful API using the different HTTP methods:
  • GET to retrieve and search data
  • POST to add data
  • PUT to update data
  • DELETE to delete data
The application used as an example for this article is a Wine Cellar app. You can search for wines, add a wine to your cellar, update and delete wines.

You can run the application here. The create/update/delete features are disabled in this online version. Use the link at the bottom of this post to download a fully enabled version.

The REST API consists of the following methods:
MethodURLAction
GET/api/winesRetrieve all wines
GET/api/wines/search/ChateauSearch for wines with ‘Chateau’ in their name
GET/api/wines/10Retrieve wine with id == 10
POST/api/winesAdd a new wine
PUT/api/wines/10Update wine with id == 10
DELETE/api/wines/10Delete wine with id == 10

Implementing the API using JAX-RS

JAX-RS makes it easy to implement this API in Java. You simply create a class defined as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package org.coenraets.cellar;
 
@Path("/wines")
public class WineResource {
 
    WineDAO dao = new WineDAO();
 
    @GET
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public List findAll() {
        return dao.findAll();
    }
 
    @GET @Path("search/{query}")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public List findByName(@PathParam("query") String query) {
        return dao.findByName(query);
    }
 
    @GET @Path("{id}")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Wine findById(@PathParam("id") String id) {
        return dao.findById(Integer.parseInt(id));
    }
 
    @POST
    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Wine create(Wine wine) {
        return dao.create(wine);
    }
 
    @PUT @Path("{id}")
    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Wine update(Wine wine) {
        return dao.update(wine);
    }
 
    @DELETE @Path("{id}")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public void remove(@PathParam("id") int id) {
        dao.remove(id);
    }
}
Quick look at the JAX-RS annotations used in this class:
  • @GET@POST@PUT@DELETE: HTTP method the class method responds to.
  • @Path: path the method responds to.
  • @Consumes: type of data the method can take as input. The data will automatically be deserialized into a method input parameter. For example, you can pass a wine object to the addWined() method either as JSON or XML. The JSON or XML representation of a new wine is automatically deserialized into the Wine object passed as an argument to the method.
  • @Produces: One or more response content type(s) the method can generate. The method’s return value will be automatically serialized using the content type requested by the client. If the client didn’t request a specific content type, the first content type listed in the @Produces annotation will be used. For example, if you access http://coenraets.org/rest/wines, you get a list of wines represented as JSON because it is the first content type listed in the @Produces annotation of the findAll() method.
The jQuery client below sends data to the server using JSON (addWine() and updateWine() methods).
The approach you use to actually retrieve the data is totally up to you. In this example, I use a simple DAO, but you can of course use your own data access solution.

Testing the API using cURL

If you want to test your API before using it in a client application, you can invoke your REST services straight from a browser address bar. For example, you could try:
You will only be able to test your GET services that way, and even then, it doesn’t give you full control to test all the content types your API can return.
A more versatile solution to test RESTful services is to use cURL, a command line utility for transferring data with URL syntax.
For example, using cURL, you can test the Wine Cellar API with the following commands:
  • Get all wines returned as default content type:
    curl -i -X GET http://localhost:8080/cellar/rest/wines
  • Get all wines returned as xml:
    curl -i -X GET http://localhost:8080/cellar/rest/wines -H 'Accept:application/xml'
  • Get all wines with ‘chateau’ in their name:
    curl -i -X GET http://localhost:8080/cellar/rest/wines/search/chateau
  • Get wine #5:
    curl -i -X GET http://localhost:8080/cellar/rest/wines/5
  • Delete wine #5:
    curl -i -X DELETE http://localhost:8080/cellar/rest/wines/5
  • Add a new wine:
    curl -i -X POST -H 'Content-Type: application/json' -d '{"name": "New Wine", "year": "2009"}' http://localhost:8080/cellar/rest/wines
  • Modify wine #27:
    curl -i -X PUT -H 'Content-Type: application/json' -d '{"id": "27", "name": "New Wine", "year": "2010"}' http://localhost:8080/cellar/rest/wines/27

The jQuery Client

Accessing your API through cURL is cool, but there is nothing like a real application to put your API to the test. So the source code (available for download at the end of this post) includes a simple jQuery client to manage your wine cellar.
Here is the jQuery code involved in calling the services:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
function findAll() {
    $.ajax({
        type: 'GET',
        url: rootURL,
        dataType: "json", // data type of response
        success: renderList
    });
}
 
function findByName(searchKey) {
    $.ajax({
        type: 'GET',
        url: rootURL + '/search/' + searchKey,
        dataType: "json",
        success: renderList
    });
}
 
function findById(id) {
    $.ajax({
        type: 'GET',
        url: rootURL + '/' + id,
        dataType: "json",
        success: function(data){
            $('#btnDelete').show();
            renderDetails(data);
        }
    });
}
 
function addWine() {
    console.log('addWine');
    $.ajax({
        type: 'POST',
        contentType: 'application/json',
        url: rootURL,
        dataType: "json",
        data: formToJSON(),
        success: function(data, textStatus, jqXHR){
            alert('Wine created successfully');
            $('#btnDelete').show();
            $('#wineId').val(data.id);
        },
        error: function(jqXHR, textStatus, errorThrown){
            alert('addWine error: ' + textStatus);
        }
    });
}
 
function updateWine() {
    $.ajax({
        type: 'PUT',
        contentType: 'application/json',
        url: rootURL + '/' + $('#wineId').val(),
        dataType: "json",
        data: formToJSON(),
        success: function(data, textStatus, jqXHR){
            alert('Wine updated successfully');
        },
        error: function(jqXHR, textStatus, errorThrown){
            alert('updateWine error: ' + textStatus);
        }
    });
}
 
function deleteWine() {
    console.log('deleteWine');
    $.ajax({
        type: 'DELETE',
        url: rootURL + '/' + $('#wineId').val(),
        success: function(data, textStatus, jqXHR){
            alert('Wine deleted successfully');
        },
        error: function(jqXHR, textStatus, errorThrown){
            alert('deleteWine error');
        }
    });
}
 
// Helper function to serialize all the form fields into a JSON string
function formToJSON() {
    return JSON.stringify({
        "id": $('#id').val(),
        "name": $('#name').val(),
        "grapes": $('#grapes').val(),
        "country": $('#country').val(),
        "region": $('#region').val(),
        "year": $('#year').val(),
        "description": $('#description').val()
        });
}

Download the Source Code

The source code for this application is hosted on GitHub here. And here is a quick link to the project download (Eclipse Dynamic Web Project). It includes both the Java and jQuery code for the application.
UPDATE (1/11/2012): A version of this application using Backbone.js at the client-side is also available on GitHub here. You can find more information on the Backbone.js of this application here.
I’m interested in your feedback. Let me know what you think and what your experience has been building RESTful-based applications using Java and jQuery.

Share this Article:

5 comments:

oakleyses said...

louis vuitton handbags, oakley sunglasses, louboutin, longchamp outlet, nike shoes, louis vuitton outlet stores, chanel handbags, burberry outlet, prada outlet, jordan shoes, tiffany and co, michael kors outlet, tory burch outlet, louis vuitton outlet, longchamp handbags, nike free, true religion jeans, michael kors outlet, kate spade outlet, polo ralph lauren outlet, tiffany and co, prada handbags, polo ralph lauren outlet, michael kors outlet, michael kors outlet, longchamp handbags, oakley sunglasses, ray ban sunglasses, kate spade handbags, burberry outlet, louis vuitton outlet, louboutin outlet, louboutin, coach factory outlet, air max, air max, coach outlet, gucci outlet, christian louboutin shoes, michael kors outlet, coach purses, ray ban sunglasses, michael kors outlet, louis vuitton, coach outlet store online, true religion jeans, oakley sunglasses cheap

oakleyses said...

ralph lauren, lululemon, air max, hollister, north face, nike air max, polo lacoste, vanessa bruno, timberland, vans pas cher, louboutin, louis vuitton, oakley pas cher, air max pas cher, nike roshe run, air max, true religion outlet, barbour, sac longchamp, air force, hollister, sac louis vuitton, nike free, polo ralph lauren, nike trainers, louis vuitton uk, nike roshe, sac hermes, longchamp, michael kors, sac burberry, sac guess, mulberry, new balance pas cher, converse pas cher, sac louis vuitton, hogan outlet, nike tn, north face, true religion outlet, ray ban pas cher, michael kors, air jordan, nike blazer, nike free pas cher, michael kors pas cher, abercrombie and fitch, ray ban sunglasses

oakleyses said...

mac cosmetics, mont blanc, marc jacobs, canada goose outlet, nike huarache, vans shoes, soccer jerseys, hollister, giuseppe zanotti, beats by dre, abercrombie and fitch, longchamp, insanity workout, celine handbags, bottega veneta, ghd, nfl jerseys, north face outlet, chi flat iron, ugg boots, birkin bag, ugg australia, canada goose, herve leger, ugg pas cher, rolex watches, valentino shoes, canada goose uk, canada goose, ferragamo shoes, canada goose, ugg boots, uggs outlet, north face jackets, soccer shoes, asics running shoes, new balance shoes, p90x, lululemon outlet, canada goose jackets, mcm handbags, instyler, babyliss pro, ugg, wedding dresses, jimmy choo outlet, reebok outlet, nike roshe run

oakleyses said...

parajumpers, karen millen, air max, converse, pandora charms, moncler, louboutin, moncler, links of london, lancel, juicy couture outlet, oakley, hollister, pandora charms, supra shoes, thomas sabo, canada goose, gucci, wedding dresses, timberland boots, swarovski crystal, air max, coach outlet store online, moncler, ray ban, canada goose, moncler, ugg, louis vuitton, swarovski, hollister, montre homme, moncler, hollister clothing store, ralph lauren, rolex watches, moncler outlet, moncler, iphone 6 cases, baseball bats, juicy couture outlet, toms shoes, vans, pandora jewelry, ugg, converse shoes

prathap kumar said...

Great & Useful Articles

Backbone.js Course
BackboneJS Training
BackboneJS Online Training
BackboneJS Training in Chennai