12.2. Using REST in Java

Issuing HTTP GET Requests
The key class here is HttpURLConnection, obtained by invoking openConnection on a URL object. Sadly, openConnection method's signature specifies that it returns the superclass type, URLConnection, and we have to downcast the result.

The following method issues a request and returns the entire response as one long string:

public static String httpGet(String urlStr) throws IOException {
  URL url = new URL(urlStr);
  HttpURLConnection conn =
      (HttpURLConnection) url.openConnection();

  if (conn.getResponseCode() != 200) {
    throw new IOException(conn.getResponseMessage());
  }

  // Buffer the result into a string
  BufferedReader rd = new BufferedReader(
      new InputStreamReader(conn.getInputStream()));
  StringBuilder sb = new StringBuilder();
  String line;
  while ((line = rd.readLine()) != null) {
    sb.append(line);
  }
  rd.close();

  conn.disconnect();
  return sb.toString();
}

(This code is very raw; it should be properly padded with try/catch/finally blocks to ensure the reader is closed, etc.)

Remember that if the request URL includes parameters, they must be properly encoded (e.g., a space is %20, etc.). The class URLEncoder can be used to perform this encoding.

Issuing HTTP POST Requests
URL encoding is also required for POST requests, as shown in the following method:

public static String httpPost(String urlStr, String[] paramName,
String[] paramVal) throws Exception {
  URL url = new URL(urlStr);
  HttpURLConnection conn =
      (HttpURLConnection) url.openConnection();
  conn.setRequestMethod("POST");
  conn.setDoOutput(true);
  conn.setDoInput(true);
  conn.setUseCaches(false);
  conn.setAllowUserInteraction(false);
  conn.setRequestProperty("Content-Type",
      "application/x-www-form-urlencoded");

  // Create the form content
  OutputStream out = conn.getOutputStream();
  Writer writer = new OutputStreamWriter(out, "UTF-8");
  for (int i = 0; i < paramName.length; i++) {
    writer.write(paramName[i]);
    writer.write("=");
    writer.write(URLEncoder.encode(paramVal[i], "UTF-8"));
    writer.write("&");
  }
  writer.close();
  out.close();

  if (conn.getResponseCode() != 200) {
    throw new IOException(conn.getResponseMessage());
  }

  // Buffer the result into a string
  BufferedReader rd = new BufferedReader(
      new InputStreamReader(conn.getInputStream()));
  StringBuilder sb = new StringBuilder();
  String line;
  while ((line = rd.readLine()) != null) {
    sb.append(line);
  }
  rd.close();

  conn.disconnect();
  return sb.toString();
}

As you can see, it's not a pretty site (and that's before adding proper try/catch/finally structures). The problem is that, out of the box, Java's support for handling web connections is pretty low-level.

A good solution can be found in the popular Apache Commons library, and in particular the httpclient set of packages. See Yahoo! guide to REST with Java for details and examples. The documentation covers several interesting extras, such as caching.

8 comments:

Naga said...

The example shows making a request to read the rest response. Can you please add some examples on generating the response content for the said url?

Dr. M. Elkstein said...

Hello Naga,

There's the question of how to generate a response, and of what to put in the response.

The responses are generated by a web server. So, for "how", there are multiple alternatives in Java; for example, one could use Tomcat, or JBoss, and Servlets, or JSPs, or any other templating engine, or generate output directly without templates (which is often more appropriate for JSON and XML responses), etc.

For "what", this really depends on your application. For example, if the REST request is for information about an item from the catalog, the response can be an XML representation of that item (see, for example, REST server responses in this tutorial).

Martin Wildam said...

You don't mention jersey - I found on the net indicators that the easiest way to consume a RESTful web service is to use JAX-RS / Jersey - but unfortunately I did not find yet a good example showing such an implementation. What is your opinion - whould I prefer to use the plain method or use such other libraries on top?

Martin Wildam said...

RESTlet also seems a library/framework for RESTful web services. Thanks to your tutorial I started to think REST is easy but even if it is intended to be easy, thinking about a concrete implementation I get unsure about the proper way using the newest technologies.

Dr. M. Elkstein said...

Hi Martin,

I'm not familiar with the frameworks you mention -- my apologies. In general, with REST, I'd go for the most lightweight framework I could find; but that's just a rule-of-thumb, and the real answer depends on your specific project requirements.

Martin Wildam said...

Thank you for your response. Not sure about what you mean with "lightweight". I am considering my whole applications to be lightweight - even servers and clients.

I feel the Java application server stack sometimes like a ball and chain.

From that point of view Restlet seems to be attractive because bringing everything with it. On the other hand I read about "compatibility" issues in relation to jersey.

Here are a few interesting links:
- Re: Restlet vs other 'rest' frameworks questions/views
http://www.mail-archive.com/discuss@restlet.tigris.org/msg07028.html

- Re: Restlet vs RestEasy
http://www.mail-archive.com/discuss@restlet.tigris.org/msg07565.html

- Which is the best Java REST API - Restlet or Jersey? - Stack Overflow
http://stackoverflow.com/questions/1710199/which-is-the-best-java-rest-api-restlet-or-jersey

- Nabble - Restlet Discuss - Restlet vs. Jersey
http://n2.nabble.com/Restlet-vs-Jersey-td3521911.html

- Nabble - Restlet Discuss - Restlet vs other 'rest' frameworks questions/views
http://n2.nabble.com/Restlet-vs-other-rest-frameworks-questions-views-td2199219.html

- Restlet - Features
http://www.restlet.org/about/features

I tend to choose Restlet over Jersey - those two are my final candidates - but I will go for lunch now and decide then.

Martin Wildam said...

Currently I need to write a REST-Client and I started with RESTlet. Although it might be a good choice when implementing REST services, I decided (after already done the first steps) to change and return to root method(s) as described in your wonderful Tutorial here.

You said: "In general, with REST, I'd go for the most lightweight framework I could find". - This is in fact an experience I made for other components too. - Maybe just "lightweight" should be replaced with "straightforward".

Indicator Veritatis said...

The Java 5 API on classes URI and URL do not recommend using URLEncoder for this purpose.

In fact, URLEncoder does a slightly different kind of 'encoding' HTML encoding. But the RFC calls for URL scaping, not HTML encoding.

The recommended approach is to use a URI constructer, then convert to URL using toUrl.

But not every UIR constructor is equally suitable for this either. The single-argument constructor does no encoding, so any of the other constructors should be used instead.

This will take care of the URL encoding correctly, adding only a slight burden of handling one more Exception.