Posts Tagged REST

REST: DELETE operation and tunneling

I was looking at some presentation slides on REST vs SOAP and one of the major drawbacks listed for REST approach is -- lack of ability to use a message body for DELETE operations. I was not sure when I read that, why that would be a drawback.  (Discussion on the listed drawbacks will be a separate post by itself for another day).

A DELETE operation might look something like the following, where 123 is the ID of the customer:

DELETE /customers/123
Host: example.com

I was thinking about some use cases where a server might need more information for deletion. Thinking along the lines I tweeted this primarily to contest the presenter's belief that it is such a huge drawback that you use that as a strike against REST-based approach.

Subbu Allamaraju responded. He says that the the question is a valid one.  Subbu said that:

That is a good question. Think of any case where client has to explain why the resource needs to be DELETEd. This is not uncommon.

Assuming that's a valid concern I was thinking of ways to do that in a RESTy manner. Only way that I could think of at that point was whether we could use POST and perform DELETE. That sounded to me like tunneling. Tunneling is hiding operations from HTTP. There is no way to know whether the operation is -- safe, idempotent, both safe and idempotent, neither safe nor idempotent.

As we continue discussing this on Twitter I've asked how different this scenario would be from tunneling via GET, something like the following:

GET /customer?method=delete&id=123
Host: example.com

The above GET is a clear example of tunneling. Similarly, SOAP-way of POST is another good example of tunneling.

POST /CustomerService HTTP/1.1
Host: example.com
Content-Type: application/soap+xml; charset=utf-8
Content-Length: nnn

<?xml version="1.0"?>
<soap:Envelope
 xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
 soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">
    <soap:Body xmlns:m="http://example.com/customer">
        <m:deleteCustomer>
            <m:id>123</m:id>
        </m:deleteCustomer>
    </soap:Body>
</soap:Envelope>

Approach

An approach that sounded reasonable: use POST with a distinct URI when in doubt. That way you would avoid tunneling by making it a distinct resource.

POST /customers/123/deleteme
Host: example.com
Content-type: xxx

[send reasoning to the server why the resource is being deleted in the body]

This provides some visibility into the operation, via a URI that conveys the intention.

One downside of this approach is caches will not see the resource being deleted. In spite of that, this approach seems reasonable when you have a specific need to address the use case in question.

Thanks to Subbu for suggesting this approach. Provide your comments if you know of any other approaches.

P.S: Just one more reason why I like Twitter! Feel free to follow me there.

Tags:

Java’s HTTP Handler and Cache Validation Issues

Background

A little while ago I've mentioned that I was working on client-side HTTP caching (using Ehcache) for REST clients. After a little hiatus, I'm back to complete the unfinished business, precisely dealing with cache validation support (using ETag, Last-Modified, If-None-Match, If-Modified-Since headers). I've also explained the approach I was taking to implement the solution, using Java's ResponseCache mechanism.

However, I think I've hit a dead end implementing the solution using that approach. I will try to explain it here and hope that smarter people out there provide their thoughts.

Overview

Let's start with a simple straightforward scenario. Quick control flow of the Java protocol handler approach:

  1. A client application gets an instance of sun.net.www.protocol.http.HttpUrlConnection, which extends java.net.HttpUrlConnection, via url.openConnection(). This hanlder instantiates a registered instance of java.net.ResponseCache, if there is one available.
  2. When a request is sent to the server via HttpUrlConnection, protocol handler first checks whether the representation is present in the cache by calling the get() method of the ResponseCache. If it is in the cache send it to the client, else send the request to the origin server.
  3. If the request is sent to the origin server, and if the response is any of 200, 301, or other "cache-able" statuses, the handler then calls the put() method of the ResponseCache to potentially cache the representation.
  4. ResponseCache would store the element in the cache. It uses Expires, Date, Cache-Control headers to determine time to live and set it on the element. Let's ignore expiration model for this post as the focus is on validation.

Note: You have to write your own concrete implementation for ResponseCache to store and retrieve elements from the cache. Java doesn't provide an out-of-the-box implementation for it, but it provides a framework for doing so.

Validating Cached Element

Now let’s look at a scenario that does cache validation. First, what is validation? There are two headers a server may send for validating the resource: a timestamp (Last-Modified) indicating when the resource was last changed, and an entity tag value (ETag). Server may choose to send only one of these headers, as both of them try to achieve the same purpose.

Responding to a request for resource X, a server sends along one or both these headers to the client along with X’s representation. On any subsequent request for resource X -- the client may honor these response headers, and sends two of its own headers: If-Modified-Since (with the value of the Last-Modified header) and If-None-Match (with the value of ETag header). Former requests the server to send the representation only if the resource is modified since the Last-Modified time it has got, and the latter asks to send the representation only on the change of ETag value that it supplied.

If there is a change in the resource, a server sends an updated representation, with new values for ETag and/or Last-Modified headers. This scenario works fine with no issues as you get a 200 response back, and the protocol handler handles this just fine (similar to the straightforward scenario mentioned above). The issue that I'm going to mention is with the case in which the server determines that there is no change with the resource, and sends back a status 304, NOT MODIFIED, with no body in the content.

See the following sequence of events that end up with a status code 304 from the server (click on the image to enlarge):

Conditional GET

Issues with Java's HTTP Handler

  • A client or client-side cache should first check whether a cached representation is available before sending a conditional GET of this sort. (There is no point sending Not-Modified-Since and/or If-None-Match headers if it doesn’t have a representation to fall back on). Java’s cache handler framework using HttpUrlConnection does not provide an option to do so.Let's see the relevant source code of sun.net.www.protocol.http.HttpUrlConnection, lines 399-410:
    // Set modified since if necessary
    long modTime = getIfModifiedSince();
    if (modTime != 0) {
        Date date = new Date(modTime);
        //use the preferred date format according to RFC 2068(HTTP1.1),
        // RFC 822 and RFC 1123
        SimpleDateFormat fo = new SimpleDateFormat(
            "EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
        fo.setTimeZone(TimeZone.getTimeZone("GMT"));
        requests.setIfNotSet("If-Modified-Since", fo
            .format(date));
    }
    

    The above block of code adds If-Modified-Since header but makes no checks whatsoever whether the representation is available in the cache.

  • I don't see a reference to If-None-Match header in the source. So if the client sends that header, it will be sent to the origin server without an availability check
  • In case, if there is no representation in the cache, the cache must have an ability to remove the validation headers from the request before sending the request to the origin server. I don't see this framework supporting such a behavior.

Thoughts??

// Set modified since if necessary
0400:                    long modTime = getIfModifiedSince();
0401:                    if (modTime != 0) {
0402:                        Date date = new Date(modTime);
0403:                        //use the preferred date format according to RFC 2068(HTTP1.1),
0404:                        // RFC 822 and RFC 1123
0405:                        SimpleDateFormat fo = new SimpleDateFormat(
0406:                                "EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
0407:                        fo.setTimeZone(TimeZone.getTimeZone("GMT"));
0408:                        requests.setIfNotSet("If-Modified-Since", fo
0409:                                .format(date));
0410:                    }

Tags: , ,

REST: CRUD with Grails

Several weeks ago I have posted my experiences with JAX-RS. Later on, as I read and understood more about Grails, I wanted to perform a similar CRUD exercise with Grails. Grails supports REST out of the box. If somebody says that Grails rocks, you don't have to take them at their word but try out a few examples like this (and eventually agree with them!).

Obviously, REST is lot more than CRUD, and an excellent architectural pattern in my opinion. But for this post we will restrict ourselves to the CRUD aspect.

Get started

Downloading and setting up Grails is out of scope for this post. Refer to Grails website for that. Once you have Grails setup run

grails create-app [app-name]

Replace [app-name] with what ever you want to name your app. This command should create a standard Grails project structure. We will use the embedded database (HSQLDB) and embedded servlet container (Jetty). Grails ships with them auto-configured. So use all defaults for this exercise.

Resource

The resource we will be dealing here is a simple Customer object with firstName, lastName and zipcode properties. Declare Customer.groovy in grails-app/domain.

class Customer {
  String firstName
  String lastName
  String zipcode

  static constraints = {
    firstName(blank: false)
    lastName(blank: false)
    zipcode(blank: false)
  }
}

All the properties are made mandatory, as specified in the constraints above.

URL Mappings

URL mappings in Grails is an extremely useful and powerful feature. We can set a URL mapping's action parameter to a map that associates HTTP methods with action names. UrlMappings.groovy will go into grails-app/conf

class UrlMappings {
    def mappings = {
      "/customer" (controller: "customer") {
        action = [GET:"list", POST: "create"]
      }

      "/customer/$id" (controller: "customer") {
        action = [GET: "show", PUT:"update", DELETE:"delete"]
      }
	}
}

Here we have mapped /customer to a corresponding controller (we still have to work on the Controller, coming next), and the actions are mapped such that GET requests go to list action of the controller and POST is forwarded to save action. For a different URL pattern /customer/$id we have also provided mappings for its supported HTTP methods: GET, PUT and DELETE.

Controller

Create CustomerController.groovy in grails-app/controller, and let's start working on CRUD operations on our resource.

Create

import grails.converters.XML

class CustomerController {
  def create = {
    def xml = request.XML
    def customer = new Customer()
    customer.firstName = xml.firstName.text()
    customer.lastName = xml.lastName.text()
    customer.zipcode = xml.zipcode.text()

    if (customer.validate()) {
      customer.save();
      response.status = 201
      render customer as XML
    } else {
      sendValidationFailedResponse(customer, 403)
    }
  }

  private def sendValidationFailedResponse(customer, status) {
    response.status = status
    render contentType: "application/xml", {
      errors {
        customer?.errors?.fieldErrors?.each {err ->
          field(err.field)
          message(g.message(error: err))
        }
      }
    }
  }
}
  • See the power of Groovy, the ease with which you can navigate XML. Populate the Customer object and validate it for constraints defined in the domain object.
  • If the validation is passed save the resource, set the status and respond back with the created resource (with id). In this context, see line 14. It can't get any simpler than that. A domain object is transformed to XML with just that call.
  • Grails ships with two converters XML and JSON. If you want JSON response above just replace line 14 with render customer as JSON
  • rest of the code is status setting and error handling, a display of Groovy closures.

Test it
First, run your app from the command line --

grails run-app

Rest-client is an excellent REST client, I strongly recommend it for standalone testing. It has got a nice little UI and works as advertised with little fuss.

Create Customer Resource

(Click on the image to enlarge)

Read

  def list = {render Customer.list() as XML}

  def show = {
    Customer customer = Customer.get(params.id)
    if (customer) {
      render customer as XML
    } else {
      SendNotFoundResponse()
    }
  }

  private def SendNotFoundResponse() {
    response.status = 404
    render contentType: "application/xml", {
      errors {
        message("Customer not found with id: " + params.id)
      }
    }
  }
  • It doesn't get much simpler than that. Two actions list and show display XML but act on two different URL patterns (see URL mappings). Former lists all the customers in the database and the latter provide details of a specific customer (with a given id).
  • Note that id portion is passed on using params.id field
  • Status code 404 is sent back, see SendNotFoundResponse. Response is sent back in XML.

Test it
GET is easy to test in a browser, or use Rest-client to test. See the URL, client is requesting for a resource with an Id.

Get Customer Resource

(Click on the image to enlarge)

Update
Update and Delete are very similar to the code described above. The following should be self explanatory by now.

  def update = {
    Customer customer = Customer.get(params.id)
    if (!customer) {
      SendNotFoundResponse()
    }

    def xml = request.XML
    customer.firstName = xml.firstName.text()
    customer.lastName = xml.lastName.text()
    customer.zipcode = xml.zipcode.text()

    if (customer.validate()) {
      customer.save();
      response.status = 201
      render ""
    } else {
      sendValidationFailedResponse(customer, 403)
    }
  }

Delete

  def delete = {
    Customer customer = Customer.get(params.id)
    if (!customer) {
      SendNotFoundResponse()
    }
    customer.delete();
    response.status = 204
    render ""
  }

Tags: ,

RESTful API and HATEOAS Conundrum

HATEOAS --- Hypermedia as the Engine of Application State is an important constraint of REST architecture as defined by Roy Fielding. To better understand the principle I've been discussing the topic with various REST enthusiasts. In this post I would like to provide an update on some of the learning points. There are arguably quite a few false claims of RESTful-ness (by API providers) while not following some of the basic tenets of REST. HATEOAS seems to be one of the main principles that is either misunderstood or not implemented in its true spirit.

Hypermedia and Application State

There are two important pieces here:

  • What is Hypermedia?:  Just as Hypertext documents contains links to other chunks of text, Hypermedia links to many media types including -- text, images, audio, video, etc. WWW is a common example of Hypermedia.
  • What is application state?: Not to be mistaken with the resource state. Application state is the state of the user's application of computing to a given task. This is an important distinction, as Mark Baker explains in this InfoQ article:

"application state" refers to the state that determines "where" the user is in the process of completing a task. For example, when doing personal banking, is the user currently viewing account balances, filling in a bill payment form, or about to order new cheques? Those are each different application states. Some people mistakenly believe that "state" here refers to resource state, which would include, in this example, the balance of the accounts or the list of recent payments made. That isn't the case.

HATEOAS achieves two very important aspects --- protocol is not stateful, and loose coupling of client-server can be achieved as the server evolves describing new URIs.

Source of some confusion

Roy Fielding published a post in an effort to clarify what a true RESTful API is and how people are abusing the term. The last bullet point of the post says that ..

A REST API should be entered with no prior knowledge beyond the initial URI (bookmark) and set of standardized media types that are appropriate for the intended audience (i.e., expected to be understood by any client that might use the API). ...

That along with some related discussions raised some interesting questions in my mind about how much prior knowledge that a client can have.

Ask an Expert

I have asked Subbu Allamaraju a couple of questions in this regard (Subbu is well known in this area, I learnt a lot from his blog posts and articles. He works at Yahoo developing standards, patterns and practices for HTTP web languages, and is extremely passionate about REST).

Question: How much information should the client know? If I strictly go by Fielding, it should only know the start page, and server should take care of the rest, driving the flow?

Subbu: That may be possible, provided the client knows all the link relations and media types. But there is no such constraint as having one staring URI. The idea is to keep representations contextual, but not necessarily to limit the number of prepublished URIs to one. When a representation is contextual, the client can decouple itself from server's business rules.

In other words, what a client can do on a given resource depends on some business rules on the server. Instead of the client guessing that it can do certain action on a given resource, it can rely on the links contained in the representation. That is, by embedding application state and context into representations, servers can avoid leaking business rules to clients. That is the main benefit of HATEOAS for client-server apps.

[Next question could sound a bit extreme to some folks, but I read the idea earlier and so wanted find Subbu's take on that.]

Question: Is it practical to say that the client shouldn't know anything including the fields that are being worked on (say username and password fields of the form) and shouldn't know about what page it is posting to, say "/login".

Subbu: This may be not practical and can be prohibitively expensive.

Media types, link relations and things like microformats can help to a certain extent. In the case of XHTML representations containing well-known microformats, you may be able to minimize the amount of information a client will need to know to intelligently interact with a RESTful server. Even on the web, given a perfectly described XHTML, try replacing a human with a perl program. How far can that perl script go? Can it pass the Turing test?

If automation and self-discovery is the goal, of course, it may be worth trying. But I doubt if every application needs that capability.

[Subbu's Describing RESTful applications is an excellent article that explains HATEOAS and more. Highly recommended]

Next Steps

Along the lines I will be closely following Solomon Duskis' thought process to see what his experiments result in. Solomon and I had some good conversations, and his posts helped provoke some thought.

I guess the best way to understand this is by reviewing a good example. I've asked the following question on Twitter

If asked about an example of truly RESTful API, which (publicly available) API do you point to? Interested: no-state-on-server, HATEOAS.

.. for which I got a couple of responses --- Amazon S3 and NetFlix. Did some research and found some good reviews on NetFlix API, so will explore that further ..

Tags: ,

REST: CRUD with JAX-RS (Jersey)

In an earlier post I've discussed setting up Tomcat and Eclipse environment for JAX-RS. This post is an attempt to discuss some more basics of JAX-RS, specifically performing CRUD operations.

Prerequisites

  • Setup environment, discussed in this post
  • Commons-HttpClient is used for the client program. Commons-codec and Commons-logging are its dependencies. All of them can be downloaded from Apache Commons website. Put those jars in the web application's lib.

Now some fun

If you are remotely interested in REST read Roy Fielding's dissertation on the topic. The chapter on REST is a must read, not necessary to understand this exercise but strongly recommended to gain a big picture.

As a part of the CRUD (creating, reading, updating and deleting) exercise let's try and create a Customer resource, retrieve Customer details, update the details and finally delete the Customer. To keep it simple and to keep the focus on the topic at hand I will not use a real database, instead use a file system leveraging Java's Properties mechanism.

Our customer POJO has got 4 properties: id, first name, last name and a zip code.

public class Customer {

	private Integer customerId;

	private String firstName;

	private String lastName;

	private String zipcode;

        //setters and getters
}

Create

Let's start with CustomerResource class. A resource class is a Java class that uses JAX-RS annotations to implement corresponding Web resource. According to the specification, resource classes are POJOs that have at least one method annotated with @Path or a request method designator.

@Path ("customer")
public class CustomerResource {
    public static final String DATA_FILE = "C:\\TEMP\\customer-data.txt";

    @POST
    @Consumes ("application/xml")
    public Response addCustomer(InputStream customerData) {
    try {
        Customer customer = buildCustomer(null, customerData);
        long customerId = persist(customer, 0);
        return Response.created(URI.create("/" + customerId)).build();
    } catch (Exception e) {
        throw new WebApplicationException(e, Response.Status.INTERNAL_SERVER_ERROR);
    }
}

  • Paths are relative. For an annotated class the base URI is the application context. For an annotated method the base URI is the effective URI of the contatining class. Base URIs are treated as if they ended in "/". So in the above example first line indicates how the Customer URI is mapped to /customer.
  • addCustomer method is annotated as a method that responds to the HTTP POST method calls. As there is no @Path annotation on the method, the URI of the class is also the URI to access the method.
  • Another important annotation is Consumes. It defines the media types that the methods of a resource class can accept. If not specified, it is assumed that any media type is acceptable. We don't have a Consumes annotation at the class level in this example, but if there is one the method level annotation takes precedence over the class level annotation.
  • Response is returned to the client which contains a URI to the newly created resource. Return type Response results in an entity body mapped from the entity property of the Response with the status code specified by the status property of the response.
  • WebApplicationException is a RuntimeException that is used to wrap the appropriate HTTP status codes.
  • addCustomer method builds the customer from the received XML input and persists it. Code for the methods called in addMethod() is provided below and is self-explanatory. These methods are used for both create and update purposes and hence the method signatures take Customer as a parameter.

    private long persist(Customer customer, long customerId) throws IOException {
        Properties properties = new Properties();
        properties.load(new FileInputStream(DATA_FILE));

        if (customerId == 0) {
            customerId = System.currentTimeMillis();
        }

        properties.setProperty(String.valueOf(customerId),
            customer.getFirstName() + "," + customer.getLastName() +
            "," + customer.getZipcode());
        properties.store(new FileOutputStream(DATA_FILE), null);
        return customerId;
    }

    private Customer buildCustomer(Customer customer, InputStream customerData)
        throws ParserConfigurationException, SAXException, IOException {
        if (customer == null) {
            customer = new Customer();
        }
        DocumentBuilder documentBuilder =
            DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document document = documentBuilder.parse(customerData);
            document.getDocumentElement().normalize();

            NodeList nodeList = document.getElementsByTagName("customer");

            Node customerRoot = nodeList.item(0);
            if (customerRoot.getNodeType() == Node.ELEMENT_NODE) {
                Element element = (Element) customerRoot;
                NodeList childNodes = element.getChildNodes();
                for (int i = 0; i < childNodes.getLength(); i++) {
                    Element childElement = (Element)childNodes.item(i);
                    String tagName = childElement.getTagName();
                    String textContent = childElement.getTextContent();
                    if (tagName.equals("firstName")) {
                        customer.setFirstName(textContent);
                    } else if (tagName.equals("lastName")) {
                        customer.setLastName(textContent);
                    } else if (tagName.equals("zipcode")) {
                        customer.setZipcode(textContent);
                    }
                }
            } else {
                throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
            }

            return customer;
    }
Test it

A test client method to test add operation looks something like the following, you may run this as a Java application via a main() method or it is easy to modify the following as a Junit test case.

    private static void testAddCustomer() throws IOException, HttpException {
        final String addCustomerXML =
            "<customer>" +
                "<firstname>Joe</firstname>" +
                "<lastname>Schmo</lastname>" +
                "<zipcode>98042</zipcode>" +
            "</customer>";

        PostMethod postMethod = new PostMethod(
            "http://localhost:9000/RestExample/customer");
        RequestEntity entity = new InputStreamRequestEntity(
            new ByteArrayInputStream(addCustomerXML.getBytes()),
            "application/xml");
        postMethod.setRequestEntity(entity);
        HttpClient client = new HttpClient();
        try {
            int result = client.executeMethod(postMethod);
            System.out.println("Response status code: " + result);
            System.out.println("Response headers:");
            Header[] headers = postMethod.getResponseHeaders();
            for (int i = 0; i < headers.length; i++) {
            System.out.println(headers[i].toString());
        } finally {
            postMethod.releaseConnection();
        }
    }
Output


Response status code: 201
Response headers:
Server: Apache-Coyote/1.1
Location: http://localhost:9000/RestExample/customer/1236708444823
Content-Length: 0
Date: Thu, 05 Mar 2009 12:15:22 GMT

  • Response status code 201 indicates the resource was created
  • Location header displays the URI of the new resource that is created

Read

retrieveCustomer() method below illustrates the read operation:

      @GET
      @Path ("{id}")
      @Produces ("application/xml")
      public StreamingOutput retrieveCustomer(@PathParam ("id") String customerId) {
          try {
              String customerDetails = loadCustomer(customerId);
              System.out.println("customerDetails: " + customerDetails);
              if (customerDetails == null) {
                  throw new NotFoundException("<error>No customer with id: " +
                      customerId + "</error>");
              }

              final String[] details = customerDetails.split(",");

              return new StreamingOutput() {
                  public void write(OutputStream outputStream) {
                      PrintWriter out = new PrintWriter(outputStream);
                      out.println("< ?xml version=\"1.0\" encoding=\"UTF-8\"?>");
                      out.println("<customer>");
                      out.println("<firstname>" + details[0] + "</firstname>");
                      out.println("<lastname>" + details[1] + "</lastname>");
                      out.println("<zipcode>" + details[2] + "</zipcode>");
                      out.println("</customer>");
                      out.close();
                  }
              };
          } catch (IOException e) {
              throw new WebApplicationException(e,
                  Response.Status.INTERNAL_SERVER_ERROR);
          }
      }
  • Get annotation indicates that this method is responsible for HTTP GET operations
  • Path annotation indicates a dynamic id. As discussed above the paths are relative, so the URI /customer/{id} is the resultant path for this method
  • Produces annotation, indicates the media type that is resulted from this operation
  • PathParam annotation in the parameter reads the path id that is passed using the Path annotation
  • StreamingOutput is returned by this resource method. StreamingOutput is a simpler version of MessageBodyWriter. It has a write() method that has Output stream. All that you need is to write the data into that object, which will be returned to the client program.
Test it

In a web browser you may try the URI that was resulted from our create operation above (http://localhost:9000/RestExample/customer/1236708444823 for this example).

Output

You should see response something like the followiing:

< ?xml version="1.0" encoding="UTF-8" ?>
- <customer>
  <firstname>Joe</firstname>
  <lastname>Schmo</lastname>
  <zipcode>98042</zipcode>
  </customer>

Update

POST is used create a new resource, PUT method updates the state of a known resource. You often see discussions about when to use POST vs PUT. My understanding is that if you are trying to create a brand new resource use POST, no Request-URI is required for POST. State is sent as a part of the BODY and the server should return you back HTTP code 201 (resource created), just like what we discussed in the CREATE section above. On the other hand, if you're updating the state of a resource use PUT. In this case you need the URI to the resource that you are trying to update.

Update code follows:

      @PUT
      @Path("{id}")
      @Consumes("application/xml")
      public void updateCustomer(@PathParam("id") String customerId,
          InputStream input) {
          try {
              String customerDetails = loadCustomer(customerId);
              if (customerDetails == null) {
                  throw new WebApplicationException(Response.Status.NOT_FOUND);
              }
              String[] details = customerDetails.split(",");
              Customer customer = new Customer();
              customer.setFirstName(details[0]);
              customer.setLastName(details[1]);
              customer.setZipcode(details[2]);
              buildCustomer(customer, input);
              persist(customer, Long.valueOf(customerId));
          } catch (Exception e) {
              throw new WebApplicationException(e,
                  Response.Status.INTERNAL_SERVER_ERROR);
          }
      }
  • PUT annotation marks the method as a resource method that handles HTTP PUT requests
  • Path and Consumes annotations are already discussed in the earlier sections
Test it

Using HttpClient the update test can be performed as follows. In this example zip code of a customer is being updated:

      private static void testUpdateCustomer() throws IOException, HttpException {
          final String addCustomerXML =
              "<customer>" +
                  "<zipcode>98043</zipcode>" +
              "</customer>";

          PutMethod putMethod = new PutMethod(
              "http://localhost:9000/RestExample/customer/1236708444823");
          RequestEntity entity = new InputStreamRequestEntity(
              new ByteArrayInputStream(addCustomerXML.getBytes()),
              "application/xml");
          putMethod.setRequestEntity(entity);
          HttpClient client = new HttpClient();
          try {
              int result = client.executeMethod(putMethod);
              System.out.println("Response status code: " + result);
              System.out.println("Response headers:");
              Header[] headers = putMethod.getResponseHeaders();
              for (int i = 0; i < headers.length; i++) {
                  System.out.println(headers[i].toString());
              }
          } finally {
              putMethod.releaseConnection();
          }
      }

Delete

The HTTP DELETE method requests the server to delete the resource identified by the request-URI.

    @DELETE
    @Path("{id}")
    public void deleteCustomer(@PathParam("id") String customerId) {
	try {
	    Properties properties = new Properties();
	    properties.load(new FileInputStream(DATA_FILE));
	    String customerDetails = properties.getProperty(customerId);
	    if (customerDetails == null) {
	        throw new WebApplicationException(Response.Status.NOT_FOUND);
	    }
	    properties.remove(customerId);
	    properties.store(new FileOutputStream(DATA_FILE), null);
	} catch (Exception e) {
		throw new WebApplicationException(e, Response.Status.INTERNAL_SERVER_ERROR);
	}
    }
  • DELETE annotation is used to mark the method as a resource method that handles the resource-delete operation.
Test it

Test code for Delete. Attempting to delete the customer resource created earlier:

    private static void testDeleteCustomer() throws HttpException, IOException {
        DeleteMethod deleteMethod = new DeleteMethod(
	    "http://localhost:9000/RestExample/customer/1236708444823");
        HttpClient client = new HttpClient();
        try {
            int result = client.executeMethod(deleteMethod);
            System.out.println("Response status code: " + result);
            System.out.println("Response headers:");
            Header[] headers = deleteMethod.getResponseHeaders();
            for (int i = 0; i < headers.length; i++) {
                System.out.println(headers[i].toString());
            }
        } finally {
            deleteMethod.releaseConnection();
        }
    }

output

Status code 204 is returned, stands for No Content. This code is used in cases where the request is successfully processed, but the response doesn't have a message body


Response status code: 204
Response headers:
Server: Apache-Coyote/1.1
Date: Tue, 10 Mar 2009 18:34:46 GMT

Tags: ,

REST: Jersey configuration on Tomcat

Goal

I spent some time putting all these pieces together configuring Jersey on Tomcat, so hope this write-up may help somebody who intends to do the same.

Quick Background

JAX-RS is an annotation-based API for implementing RESTful web services. Jersey is the JAX-RS reference implementation from Sun. Jersey website says that it is more than the reference implementation; Jersey provides an API so that the developers may extend it suit their needs. There are other implementations by different vendors: RESTEasy (from JBoss), Restlet, Apache CXF.

Prerequisites

  • Download and install Tomcat (using version 6.0).
  • Download Jersey (using Jersey 1.0.2, most recent at the time of writing this)
  • I'm using Eclipse as IDE (heard that NetBeans has better REST support than Eclipse, I haven't tried it yet)

Getting Started

1. Create a new dynamic web project in Eclipse

dynamicwebproject

2. Copy Jersey jars to the web project's library:

asm-3.1.jar, jersey-core.jar, jersey-server.jar, jsr-311-api-1.0.jar

3. Deployment descriptor

Add Jersey Servlet declaration to the web.xml of the web application (this step took a little while to figure out exact class name of the jersey implementation)

    <servlet>
           <servlet-name>JerseyTest</servlet-name>
            <servlet-class>
                com.sun.jersey.spi.container.servlet.ServletContainer
            </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
           <servlet-name>JerseyTest</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

4.Resource class

Create a resource class, HelloWorldResource. See the power of JAX-RS and Jersey in the annotations below


package com.suryasuravarapu.jersey;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path ("/helloworld")
public class HelloWorldResource {

@GET
@Produces ("text/plain")
public String sayHello() {
return "Hello World";
}
}

- Line 7: @Path indicates what URI path is mapped to the resource.
- Line 11:@GETindicates what HTTP method is allowed for this resource.
-Line 12: @Produces indicates what MIME type is returned by this resource.

5. Test it

Access the resource using http://localhost:8080/RestExample/helloworld (assuming your Tomcat is running on port 8080, otherwise adjust accordingly), you should see a response back with text 'Hello World'.

This is obviously a  simple example but the focus here is mainly on the setup. Stay tuned, more RESTful stuff to come ...

Tags: ,