Java client for the hbase REST API

Modern open source projects frequently lack documentation and so does hbase regarding its REST API. Even though REST is covered in the official hbase reference book the guidelines are not complete as I’ll show in the listings below. A second source that you might have come across is a set of three blog posts by Cloudera which is quite good and much more detailled than the official hbase guidelines – but it still does not fully cover then traps you might run into when implementing a java client.

I’d like to show three examples, two for a put and one for a get request. All further operations like delete or post should then be easy to implement once you know how to send requests to the REST server.

Let us start with a simple put in which we add a row with a new row key employee1 and one single cell first_name to an existing column family natural_person (Of course you would pick names which are much shorter since each cell and column family name is stored with each cell respectively each row).

The maven dependencies I use are show in the following listing. Jersey serves as REST framework to easily create requests. Commons-codec includes the Base64 class to encode strings and jettison helps us to create json objects.

 

<dependency>
  <groupId>com.sun.jersey</groupId>
  <artifactId>jersey-client</artifactId>
  <version>1.19.1</version>
</dependency>
 
<dependency>
  <groupId>commons-codec</groupId>
  <artifactId>commons-codec</artifactId>
  <version>1.10</version>
</dependency>
 
<dependency>
  <groupId>org.codehaus.jettison</groupId>
  <artifactId>jettison</artifactId>
  <version>1.3.7</version>
</dependency>

The put we now create does not contain extra data sent with the request body but contains all information in the URL itself.

 

private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
 
public void put() throws UnsupportedEncodingException {
  Client client = Client.create();
  WebResource service = client.resource("http://myhost:8030/" + 
      URLEncoder.encode("my_namespace:MY_TABLE", UTF8_CHARSET.displayName()) + "/" + 
      URLEncoder.encode("employee1", UTF8_CHARSET.displayName()) + "/" + 
      URLEncoder.encode("natural_person:first_name", UTF8_CHARSET.displayName()) + "/" + 
      URLEncoder.encode("Jonas", UTF8_CHARSET.displayName()));
 
  ClientResponse response = service.put(ClientResponse.class);
  if (response.getStatus() == 200) {
    System.out.println("OK");
  }
}

The most important part here is to use the URLEncoder class to encode namespace, table name, row key, column family, qualifier and value. The namespace my_namespace is optional and can be ignored if you did not define any for your table. Nevertheless, I wanted to show how to place it in a request.

In the second example we will construct a complex put that is encapsulated in a json string. Using json allows us to add several cells and even several rows to a table.

public void put_complex() throws JSONException {
  WebResource wr = client.resource("http://myhost:8030/" +
      "my_namespace:MY_TABLE" + "/" +
      "fakerowkey");
 
  JSONObject root = new JSONObject();
  JSONArray rows = new JSONArray();
  JSONObject row = new JSONObject();
  row.put("key", Base64.encodeBase64String("employee1".getBytes()));
  JSONArray cells = new JSONArray();
  JSONObject cell = new JSONObject();
  cell.put("column", Base64.encodeBase64String("natural_person:first_name".getBytes()));
  cell.put("$", Base64.encodeBase64String("Jonas".getBytes()));
  cells.put(cell);
  row.put("Cell", cells);
  rows.put(row);
  root.put("Row", rows);
 
  System.out.println("JSON object is: " + root.toString());
 
  ClientResponse r = wr.type(MediaType.APPLICATION_JSON).put(ClientResponse.class, root.toString());
 
  if (r.getStatus() == 200) {
    System.out.println("OK");
  }
}

Here we see that we use the same URL as before but with a fake row key that is completely ignored by the REST API. The row key(s) used is extracted from the JSON object in the requests data section. The request we sent does exactly the same as the request before but if you like you can add an arbitrary number of cells and rows. The json object is finally added to the put as string. As before the put was successful if the status code equals 200.

Let us now look at how to do a simple get.

public void get(final String paybackBarcode) {
  WebResource service = client.resource("myhost:8030/" +
    URLEncoder.encode("my_namespace:MY_TABLE", UTF8_CHARSET.displayName()) + "/" + 
    URLEncoder.encode("employee1", UTF8_CHARSET.displayName()) + "/" +
    URLEncoder.encode("natural_person:first_name", UTF8_CHARSET.displayName()));
 
  ClientResponse response = service.get(ClientResponse.class);
  if (response.hasEntity() && response.getStatus() == 200) {
    System.out.println(response.getEntity(String.class));
  }
}

See that we now send a get request from the WebResource instead of a put. Again we encode all strings to avoid undesired chars in our URL. Regarding Jetty it is important to mention that you should call response.getEntity only once. The second time you do so it will return null.

The creation of the following requests should be easy once you know to encode all data in the URL and to base64 encode all data in json.

Leave a Reply

Your email address will not be published. Required fields are marked *