hbase-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ecl...@apache.org
Subject [23/38] HBASE-12197 Move rest to it's on module
Date Fri, 10 Oct 2014 16:53:04 GMT
http://git-wip-us.apache.org/repos/asf/hbase/blob/876617bd/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestGetAndPutResource.java
----------------------------------------------------------------------
diff --git a/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestGetAndPutResource.java b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestGetAndPutResource.java
new file mode 100644
index 0000000..c14f3e2
--- /dev/null
+++ b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestGetAndPutResource.java
@@ -0,0 +1,585 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.rest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.net.URLEncoder;
+import java.util.List;
+
+import javax.xml.bind.JAXBException;
+
+import org.apache.commons.httpclient.Header;
+import org.apache.hadoop.hbase.CompatibilityFactory;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.MediumTests;
+import org.apache.hadoop.hbase.rest.client.Response;
+import org.apache.hadoop.hbase.rest.model.CellModel;
+import org.apache.hadoop.hbase.rest.model.CellSetModel;
+import org.apache.hadoop.hbase.rest.model.RowModel;
+import org.apache.hadoop.hbase.security.User;
+import org.apache.hadoop.hbase.security.UserProvider;
+import org.apache.hadoop.hbase.test.MetricsAssertHelper;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category(MediumTests.class)
+public class TestGetAndPutResource extends RowResourceBase {
+
+  private static final MetricsAssertHelper METRICS_ASSERT =
+      CompatibilityFactory.getInstance(MetricsAssertHelper.class);
+
+  @Test
+  public void testForbidden() throws IOException, JAXBException {
+    conf.set("hbase.rest.readonly", "true");
+
+    Response response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
+    assertEquals(response.getCode(), 403);
+    response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
+    assertEquals(response.getCode(), 403);
+    response = checkAndPutValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1, VALUE_2);
+    assertEquals(response.getCode(), 403);
+    response = checkAndPutValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1, VALUE_2);
+    assertEquals(response.getCode(), 403);
+    response = deleteValue(TABLE, ROW_1, COLUMN_1);
+    assertEquals(response.getCode(), 403);
+    response = checkAndDeletePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
+    assertEquals(response.getCode(), 403);
+    response = deleteRow(TABLE, ROW_1);
+    assertEquals(response.getCode(), 403);
+
+    conf.set("hbase.rest.readonly", "false");
+
+    response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
+    assertEquals(response.getCode(), 200);
+    response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
+    assertEquals(response.getCode(), 200);
+    response = checkAndPutValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1, VALUE_2);
+    assertEquals(response.getCode(), 200);
+    response = checkAndPutValuePB(TABLE, ROW_1, COLUMN_1, VALUE_2, VALUE_3);
+    assertEquals(response.getCode(), 200);
+    response = deleteValue(TABLE, ROW_1, COLUMN_1);
+    assertEquals(response.getCode(), 200);
+    response = deleteRow(TABLE, ROW_1);
+    assertEquals(response.getCode(), 200);
+  }
+
+  @Test
+  public void testSingleCellGetPutXML() throws IOException, JAXBException {
+    Response response = getValueXML(TABLE, ROW_1, COLUMN_1);
+    assertEquals(response.getCode(), 404);
+
+    response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
+    assertEquals(response.getCode(), 200);
+    checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
+    response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2);
+    assertEquals(response.getCode(), 200);
+    checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2);
+    response = checkAndPutValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2, VALUE_3);
+    assertEquals(response.getCode(), 200);
+    checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_3);
+    response = checkAndDeleteXML(TABLE, ROW_1, COLUMN_1, VALUE_3);
+    assertEquals(response.getCode(), 200);
+
+    response = deleteRow(TABLE, ROW_1);
+    assertEquals(response.getCode(), 200);
+  }
+
+  @Test
+  public void testSingleCellGetPutPB() throws IOException, JAXBException {
+    Response response = getValuePB(TABLE, ROW_1, COLUMN_1);
+    assertEquals(response.getCode(), 404);
+    
+    response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
+    assertEquals(response.getCode(), 200);
+    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
+    response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2);
+    assertEquals(response.getCode(), 200);
+    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_2);
+
+    response = checkAndPutValuePB(TABLE, ROW_1, COLUMN_1, VALUE_2, VALUE_3);
+    assertEquals(response.getCode(), 200);
+    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_3);
+    response = checkAndPutValueXML(TABLE, ROW_1, COLUMN_1, VALUE_3, VALUE_4);
+    assertEquals(response.getCode(), 200);
+    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_4);
+
+    response = deleteRow(TABLE, ROW_1);
+    assertEquals(response.getCode(), 200);
+  }
+
+  @Test
+  public void testSingleCellGetPutBinary() throws IOException {
+    final String path = "/" + TABLE + "/" + ROW_3 + "/" + COLUMN_1;
+    final byte[] body = Bytes.toBytes(VALUE_3);
+    Response response = client.put(path, Constants.MIMETYPE_BINARY, body);
+    assertEquals(response.getCode(), 200);
+    Thread.yield();
+
+    response = client.get(path, Constants.MIMETYPE_BINARY);
+    assertEquals(response.getCode(), 200);
+    assertEquals(Constants.MIMETYPE_BINARY, response.getHeader("content-type"));
+    assertTrue(Bytes.equals(response.getBody(), body));
+    boolean foundTimestampHeader = false;
+    for (Header header: response.getHeaders()) {
+      if (header.getName().equals("X-Timestamp")) {
+        foundTimestampHeader = true;
+        break;
+      }
+    }
+    assertTrue(foundTimestampHeader);
+
+    response = deleteRow(TABLE, ROW_3);
+    assertEquals(response.getCode(), 200);
+  }
+
+  @Test
+  public void testSingleCellGetJSON() throws IOException, JAXBException {
+    final String path = "/" + TABLE + "/" + ROW_4 + "/" + COLUMN_1;
+    Response response = client.put(path, Constants.MIMETYPE_BINARY,
+      Bytes.toBytes(VALUE_4));
+    assertEquals(response.getCode(), 200);
+    Thread.yield();
+    response = client.get(path, Constants.MIMETYPE_JSON);
+    assertEquals(response.getCode(), 200);
+    assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
+    response = deleteRow(TABLE, ROW_4);
+    assertEquals(response.getCode(), 200);
+  }
+
+  @Test
+  public void testLatestCellGetJSON() throws IOException, JAXBException {
+    final String path = "/" + TABLE + "/" + ROW_4 + "/" + COLUMN_1;
+    CellSetModel cellSetModel = new CellSetModel();
+    RowModel rowModel = new RowModel(ROW_4);
+    CellModel cellOne = new CellModel(Bytes.toBytes(COLUMN_1), 1L,
+      Bytes.toBytes(VALUE_1));
+    CellModel cellTwo = new CellModel(Bytes.toBytes(COLUMN_1), 2L,
+      Bytes.toBytes(VALUE_2));
+    rowModel.addCell(cellOne);
+    rowModel.addCell(cellTwo);
+    cellSetModel.addRow(rowModel);
+    String jsonString = jsonMapper.writeValueAsString(cellSetModel);
+    Response response = client.put(path, Constants.MIMETYPE_JSON,
+      Bytes.toBytes(jsonString));
+    assertEquals(response.getCode(), 200);
+    Thread.yield();
+    response = client.get(path, Constants.MIMETYPE_JSON);
+    assertEquals(response.getCode(), 200);
+    assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
+    CellSetModel cellSet = jsonMapper.readValue(response.getBody(), CellSetModel.class);
+    assertTrue(cellSet.getRows().size() == 1);
+    assertTrue(cellSet.getRows().get(0).getCells().size() == 1);
+    CellModel cell = cellSet.getRows().get(0).getCells().get(0);
+    assertEquals(VALUE_2 , Bytes.toString(cell.getValue()));
+    assertEquals(2L , cell.getTimestamp());
+    response = deleteRow(TABLE, ROW_4);
+    assertEquals(response.getCode(), 200);
+  }
+
+  @Test
+  public void testURLEncodedKey() throws IOException, JAXBException {
+    String urlKey = "http://example.com/foo";
+    StringBuilder path = new StringBuilder();
+    path.append('/');
+    path.append(TABLE);
+    path.append('/');
+    path.append(URLEncoder.encode(urlKey, HConstants.UTF8_ENCODING));
+    path.append('/');
+    path.append(COLUMN_1);
+    Response response;
+    response = putValueXML(path.toString(), TABLE, urlKey, COLUMN_1,
+      VALUE_1);
+    assertEquals(response.getCode(), 200);
+    checkValueXML(path.toString(), TABLE, urlKey, COLUMN_1, VALUE_1);
+  }
+
+  @Test
+  public void testNoSuchCF() throws IOException, JAXBException {
+    final String goodPath = "/" + TABLE + "/" + ROW_1 + "/" + CFA+":";
+    final String badPath = "/" + TABLE + "/" + ROW_1 + "/" + "BAD";
+    Response response = client.post(goodPath, Constants.MIMETYPE_BINARY,
+      Bytes.toBytes(VALUE_1));
+    assertEquals(response.getCode(), 200);
+    assertEquals(client.get(goodPath, Constants.MIMETYPE_BINARY).getCode(),
+      200);
+    assertEquals(client.get(badPath, Constants.MIMETYPE_BINARY).getCode(),
+      404);
+    assertEquals(client.get(goodPath, Constants.MIMETYPE_BINARY).getCode(),
+      200);
+  }
+
+  @Test
+  public void testMultiCellGetPutXML() throws IOException, JAXBException {
+    String path = "/" + TABLE + "/fakerow";  // deliberate nonexistent row
+
+    CellSetModel cellSetModel = new CellSetModel();
+    RowModel rowModel = new RowModel(ROW_1);
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
+      Bytes.toBytes(VALUE_1)));
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
+      Bytes.toBytes(VALUE_2)));
+    cellSetModel.addRow(rowModel);
+    rowModel = new RowModel(ROW_2);
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
+      Bytes.toBytes(VALUE_3)));
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
+      Bytes.toBytes(VALUE_4)));
+    cellSetModel.addRow(rowModel);
+    StringWriter writer = new StringWriter();
+    xmlMarshaller.marshal(cellSetModel, writer);
+    Response response = client.put(path, Constants.MIMETYPE_XML,
+      Bytes.toBytes(writer.toString()));
+    Thread.yield();
+
+    // make sure the fake row was not actually created
+    response = client.get(path, Constants.MIMETYPE_XML);
+    assertEquals(response.getCode(), 404);
+
+    // check that all of the values were created
+    checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
+    checkValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2);
+    checkValueXML(TABLE, ROW_2, COLUMN_1, VALUE_3);
+    checkValueXML(TABLE, ROW_2, COLUMN_2, VALUE_4);
+
+    response = deleteRow(TABLE, ROW_1);
+    assertEquals(response.getCode(), 200);
+    response = deleteRow(TABLE, ROW_2);
+    assertEquals(response.getCode(), 200);
+  }
+
+  @Test
+  public void testMultiCellGetPutPB() throws IOException {
+    String path = "/" + TABLE + "/fakerow";  // deliberate nonexistent row
+
+    CellSetModel cellSetModel = new CellSetModel();
+    RowModel rowModel = new RowModel(ROW_1);
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
+      Bytes.toBytes(VALUE_1)));
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
+      Bytes.toBytes(VALUE_2)));
+    cellSetModel.addRow(rowModel);
+    rowModel = new RowModel(ROW_2);
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
+      Bytes.toBytes(VALUE_3)));
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
+      Bytes.toBytes(VALUE_4)));
+    cellSetModel.addRow(rowModel);
+    Response response = client.put(path, Constants.MIMETYPE_PROTOBUF,
+      cellSetModel.createProtobufOutput());
+    Thread.yield();
+
+    // make sure the fake row was not actually created
+    response = client.get(path, Constants.MIMETYPE_PROTOBUF);
+    assertEquals(response.getCode(), 404);
+
+    // check that all of the values were created
+    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
+    checkValuePB(TABLE, ROW_1, COLUMN_2, VALUE_2);
+    checkValuePB(TABLE, ROW_2, COLUMN_1, VALUE_3);
+    checkValuePB(TABLE, ROW_2, COLUMN_2, VALUE_4);
+
+    response = deleteRow(TABLE, ROW_1);
+    assertEquals(response.getCode(), 200);
+    response = deleteRow(TABLE, ROW_2);
+    assertEquals(response.getCode(), 200);
+  }
+
+  @Test
+  public void testStartEndRowGetPutXML() throws IOException, JAXBException {
+    String[] rows = { ROW_1, ROW_2, ROW_3 };
+    String[] values = { VALUE_1, VALUE_2, VALUE_3 };
+    Response response = null;
+    for (int i = 0; i < rows.length; i++) {
+      response = putValueXML(TABLE, rows[i], COLUMN_1, values[i]);
+      assertEquals(200, response.getCode());
+      checkValueXML(TABLE, rows[i], COLUMN_1, values[i]);
+    }
+    response = getValueXML(TABLE, rows[0], rows[2], COLUMN_1);
+    assertEquals(200, response.getCode());
+    CellSetModel cellSet = (CellSetModel)
+      xmlUnmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
+    assertEquals(2, cellSet.getRows().size());
+    for (int i = 0; i < cellSet.getRows().size()-1; i++) {
+      RowModel rowModel = cellSet.getRows().get(i);
+      for (CellModel cell: rowModel.getCells()) {
+        assertEquals(COLUMN_1, Bytes.toString(cell.getColumn()));
+        assertEquals(values[i], Bytes.toString(cell.getValue()));
+      }
+    }
+    for (String row : rows) {
+      response = deleteRow(TABLE, row);
+      assertEquals(200, response.getCode());
+    }
+  }
+
+  @Test
+  public void testInvalidCheckParam() throws IOException, JAXBException {
+    CellSetModel cellSetModel = new CellSetModel();
+    RowModel rowModel = new RowModel(ROW_1);
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
+      Bytes.toBytes(VALUE_1)));
+    cellSetModel.addRow(rowModel);
+    StringWriter writer = new StringWriter();
+    xmlMarshaller.marshal(cellSetModel, writer);
+
+    final String path = "/" + TABLE + "/" + ROW_1 + "/" + COLUMN_1 + "?check=blah";
+
+    Response response = client.put(path, Constants.MIMETYPE_XML,
+      Bytes.toBytes(writer.toString()));
+    assertEquals(response.getCode(), 400);
+  }
+
+  @Test
+  public void testInvalidColumnPut() throws IOException, JAXBException {
+    String dummyColumn = "doesnot:exist";
+    CellSetModel cellSetModel = new CellSetModel();
+    RowModel rowModel = new RowModel(ROW_1);
+    rowModel.addCell(new CellModel(Bytes.toBytes(dummyColumn),
+      Bytes.toBytes(VALUE_1)));
+    cellSetModel.addRow(rowModel);
+    StringWriter writer = new StringWriter();
+    xmlMarshaller.marshal(cellSetModel, writer);
+
+    final String path = "/" + TABLE + "/" + ROW_1 + "/" + dummyColumn;
+
+    Response response = client.put(path, Constants.MIMETYPE_XML,
+      Bytes.toBytes(writer.toString()));
+    assertEquals(response.getCode(), 404);
+  }
+
+  @Test
+  public void testMultiCellGetJson() throws IOException, JAXBException {
+    String path = "/" + TABLE + "/fakerow";  // deliberate nonexistent row
+
+    CellSetModel cellSetModel = new CellSetModel();
+    RowModel rowModel = new RowModel(ROW_1);
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
+      Bytes.toBytes(VALUE_1)));
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
+      Bytes.toBytes(VALUE_2)));
+    cellSetModel.addRow(rowModel);
+    rowModel = new RowModel(ROW_2);
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
+      Bytes.toBytes(VALUE_3)));
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
+      Bytes.toBytes(VALUE_4)));
+    cellSetModel.addRow(rowModel);
+    String jsonString = jsonMapper.writeValueAsString(cellSetModel);
+
+    Response response = client.put(path, Constants.MIMETYPE_JSON,
+      Bytes.toBytes(jsonString));
+    Thread.yield();
+
+    // make sure the fake row was not actually created
+    response = client.get(path, Constants.MIMETYPE_JSON);
+    assertEquals(response.getCode(), 404);
+
+    // check that all of the values were created
+    checkValueJSON(TABLE, ROW_1, COLUMN_1, VALUE_1);
+    checkValueJSON(TABLE, ROW_1, COLUMN_2, VALUE_2);
+    checkValueJSON(TABLE, ROW_2, COLUMN_1, VALUE_3);
+    checkValueJSON(TABLE, ROW_2, COLUMN_2, VALUE_4);
+
+    response = deleteRow(TABLE, ROW_1);
+    assertEquals(response.getCode(), 200);
+    response = deleteRow(TABLE, ROW_2);
+    assertEquals(response.getCode(), 200);
+  }
+  
+  @Test
+  public void testMetrics() throws IOException, JAXBException {
+    final String path = "/" + TABLE + "/" + ROW_4 + "/" + COLUMN_1;
+    Response response = client.put(path, Constants.MIMETYPE_BINARY,
+        Bytes.toBytes(VALUE_4));
+    assertEquals(response.getCode(), 200);
+    Thread.yield();
+    response = client.get(path, Constants.MIMETYPE_JSON);
+    assertEquals(response.getCode(), 200);
+    assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
+    response = deleteRow(TABLE, ROW_4);
+    assertEquals(response.getCode(), 200);
+
+    UserProvider userProvider = UserProvider.instantiate(conf);
+    METRICS_ASSERT.assertCounterGt("requests", 2l,
+      RESTServlet.getInstance(conf, userProvider).getMetrics().getSource());
+
+    METRICS_ASSERT.assertCounterGt("successfulGet", 0l,
+      RESTServlet.getInstance(conf, userProvider).getMetrics().getSource());
+
+    METRICS_ASSERT.assertCounterGt("successfulPut", 0l,
+      RESTServlet.getInstance(conf, userProvider).getMetrics().getSource());
+
+    METRICS_ASSERT.assertCounterGt("successfulDelete", 0l,
+      RESTServlet.getInstance(conf, userProvider).getMetrics().getSource());
+  }
+  
+  @Test
+  public void testMultiColumnGetXML() throws Exception {
+    String path = "/" + TABLE + "/fakerow";
+    CellSetModel cellSetModel = new CellSetModel();
+    RowModel rowModel = new RowModel(ROW_1);
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_1)));
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_2)));
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_3), Bytes.toBytes(VALUE_2)));
+    cellSetModel.addRow(rowModel);
+    StringWriter writer = new StringWriter();
+    xmlMarshaller.marshal(cellSetModel, writer);
+
+    Response response = client.put(path, Constants.MIMETYPE_XML, Bytes.toBytes(writer.toString()));
+    Thread.yield();
+
+    // make sure the fake row was not actually created
+    response = client.get(path, Constants.MIMETYPE_XML);
+    assertEquals(response.getCode(), 404);
+
+    // Try getting all the column values at once.
+    path = "/" + TABLE + "/" + ROW_1 + "/" + COLUMN_1 + "," + COLUMN_2 + "," + COLUMN_3;
+    response = client.get(path, Constants.MIMETYPE_XML);
+    assertEquals(200, response.getCode());
+    CellSetModel cellSet = (CellSetModel) xmlUnmarshaller.unmarshal(new ByteArrayInputStream(response
+        .getBody()));
+    assertTrue(cellSet.getRows().size() == 1);
+    assertTrue(cellSet.getRows().get(0).getCells().size() == 3);
+    List<CellModel> cells = cellSet.getRows().get(0).getCells();
+
+    assertTrue(containsCellModel(cells, COLUMN_1, VALUE_1));
+    assertTrue(containsCellModel(cells, COLUMN_2, VALUE_2));
+    assertTrue(containsCellModel(cells, COLUMN_3, VALUE_2));
+    response = deleteRow(TABLE, ROW_1);
+    assertEquals(response.getCode(), 200);
+  }
+
+  private boolean containsCellModel(List<CellModel> cells, String column, String value) {
+    boolean contains = false;
+    for (CellModel cell : cells) {
+      if (Bytes.toString(cell.getColumn()).equals(column)
+          && Bytes.toString(cell.getValue()).equals(value)) {
+        contains = true;
+        return contains;
+      }
+    }
+    return contains;
+  }
+
+  @Test
+  public void testSuffixGlobbingXMLWithNewScanner() throws IOException, JAXBException {
+    String path = "/" + TABLE + "/fakerow";  // deliberate nonexistent row
+
+    CellSetModel cellSetModel = new CellSetModel();
+    RowModel rowModel = new RowModel(ROW_1);
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
+      Bytes.toBytes(VALUE_1)));
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
+      Bytes.toBytes(VALUE_2)));
+    cellSetModel.addRow(rowModel);
+    rowModel = new RowModel(ROW_2);
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
+      Bytes.toBytes(VALUE_3)));
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
+      Bytes.toBytes(VALUE_4)));
+    cellSetModel.addRow(rowModel);
+    StringWriter writer = new StringWriter();
+    xmlMarshaller.marshal(cellSetModel, writer);
+    Response response = client.put(path, Constants.MIMETYPE_XML,
+      Bytes.toBytes(writer.toString()));
+    Thread.yield();
+
+    // make sure the fake row was not actually created
+    response = client.get(path, Constants.MIMETYPE_XML);
+    assertEquals(response.getCode(), 404);
+
+    // check that all of the values were created
+    StringBuilder query = new StringBuilder();
+    query.append('/');
+    query.append(TABLE);
+    query.append('/');
+    query.append("testrow*");
+    response = client.get(query.toString(), Constants.MIMETYPE_XML);
+    assertEquals(response.getCode(), 200);
+    assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type"));
+    CellSetModel cellSet = (CellSetModel)
+      xmlUnmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
+    assertTrue(cellSet.getRows().size() == 2);
+
+    response = deleteRow(TABLE, ROW_1);
+    assertEquals(response.getCode(), 200);
+    response = deleteRow(TABLE, ROW_2);
+    assertEquals(response.getCode(), 200);
+  }
+
+  @Test
+  public void testSuffixGlobbingXML() throws IOException, JAXBException {
+    String path = "/" + TABLE + "/fakerow";  // deliberate nonexistent row
+
+    CellSetModel cellSetModel = new CellSetModel();
+    RowModel rowModel = new RowModel(ROW_1);
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
+      Bytes.toBytes(VALUE_1)));
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
+      Bytes.toBytes(VALUE_2)));
+    cellSetModel.addRow(rowModel);
+    rowModel = new RowModel(ROW_2);
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
+      Bytes.toBytes(VALUE_3)));
+    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
+      Bytes.toBytes(VALUE_4)));
+    cellSetModel.addRow(rowModel);
+    StringWriter writer = new StringWriter();
+    xmlMarshaller.marshal(cellSetModel, writer);
+    Response response = client.put(path, Constants.MIMETYPE_XML,
+      Bytes.toBytes(writer.toString()));
+    Thread.yield();
+
+    // make sure the fake row was not actually created
+    response = client.get(path, Constants.MIMETYPE_XML);
+    assertEquals(response.getCode(), 404);
+
+    // check that all of the values were created
+    StringBuilder query = new StringBuilder();
+    query.append('/');
+    query.append(TABLE);
+    query.append('/');
+    query.append("testrow*");
+    query.append('/');
+    query.append(COLUMN_1);
+    response = client.get(query.toString(), Constants.MIMETYPE_XML);
+    assertEquals(response.getCode(), 200);
+    assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type"));
+    CellSetModel cellSet = (CellSetModel)
+      xmlUnmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
+    List<RowModel> rows = cellSet.getRows();
+    assertTrue(rows.size() == 2);
+    for (RowModel row : rows) {
+      assertTrue(row.getCells().size() == 1);
+      assertEquals(COLUMN_1, Bytes.toString(row.getCells().get(0).getColumn()));
+    }
+    response = deleteRow(TABLE, ROW_1);
+    assertEquals(response.getCode(), 200);
+    response = deleteRow(TABLE, ROW_2);
+    assertEquals(response.getCode(), 200);
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/hbase/blob/876617bd/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestGzipFilter.java
----------------------------------------------------------------------
diff --git a/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestGzipFilter.java b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestGzipFilter.java
new file mode 100644
index 0000000..23da0ec
--- /dev/null
+++ b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestGzipFilter.java
@@ -0,0 +1,161 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.rest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+import org.apache.commons.httpclient.Header;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.MediumTests;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.client.Get;
+import org.apache.hadoop.hbase.client.HBaseAdmin;
+import org.apache.hadoop.hbase.client.HTable;
+import org.apache.hadoop.hbase.client.Result;
+import org.apache.hadoop.hbase.rest.client.Client;
+import org.apache.hadoop.hbase.rest.client.Cluster;
+import org.apache.hadoop.hbase.rest.client.Response;
+import org.apache.hadoop.hbase.rest.filter.GzipFilter;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category(MediumTests.class)
+public class TestGzipFilter {
+  private static final String TABLE = "TestGzipFilter";
+  private static final String CFA = "a";
+  private static final String COLUMN_1 = CFA + ":1";
+  private static final String COLUMN_2 = CFA + ":2";
+  private static final String ROW_1 = "testrow1";
+  private static final byte[] VALUE_1 = Bytes.toBytes("testvalue1");
+
+  private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
+  private static final HBaseRESTTestingUtility REST_TEST_UTIL =
+    new HBaseRESTTestingUtility();
+  private static Client client;
+
+  @BeforeClass
+  public static void setUpBeforeClass() throws Exception {
+    TEST_UTIL.startMiniCluster();
+    REST_TEST_UTIL.startServletContainer(TEST_UTIL.getConfiguration());
+    client = new Client(new Cluster().add("localhost",
+      REST_TEST_UTIL.getServletPort()));
+    HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
+    if (admin.tableExists(TABLE)) {
+      return;
+    }
+    HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(TABLE));
+    htd.addFamily(new HColumnDescriptor(CFA));
+    admin.createTable(htd);
+  }
+
+  @AfterClass
+  public static void tearDownAfterClass() throws Exception {
+    REST_TEST_UTIL.shutdownServletContainer();
+    TEST_UTIL.shutdownMiniCluster();
+  }
+
+  @Test
+  public void testGzipFilter() throws Exception {
+    String path = "/" + TABLE + "/" + ROW_1 + "/" + COLUMN_1;
+
+    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    GZIPOutputStream os = new GZIPOutputStream(bos);
+    os.write(VALUE_1);
+    os.close();
+    byte[] value_1_gzip = bos.toByteArray();
+
+    // input side filter
+
+    Header[] headers = new Header[2];
+    headers[0] = new Header("Content-Type", Constants.MIMETYPE_BINARY);
+    headers[1] = new Header("Content-Encoding", "gzip");
+    Response response = client.put(path, headers, value_1_gzip);
+    assertEquals(response.getCode(), 200);
+
+    HTable table = new HTable(TEST_UTIL.getConfiguration(), TABLE);
+    Get get = new Get(Bytes.toBytes(ROW_1));
+    get.addColumn(Bytes.toBytes(CFA), Bytes.toBytes("1"));
+    Result result = table.get(get);
+    byte[] value = result.getValue(Bytes.toBytes(CFA), Bytes.toBytes("1"));
+    assertNotNull(value);
+    assertTrue(Bytes.equals(value, VALUE_1));
+
+    // output side filter
+
+    headers[0] = new Header("Accept", Constants.MIMETYPE_BINARY);
+    headers[1] = new Header("Accept-Encoding", "gzip");
+    response = client.get(path, headers);
+    assertEquals(response.getCode(), 200);
+    ByteArrayInputStream bis = new ByteArrayInputStream(response.getBody());
+    GZIPInputStream is = new GZIPInputStream(bis);
+    value = new byte[VALUE_1.length];
+    is.read(value, 0, VALUE_1.length);
+    assertTrue(Bytes.equals(value, VALUE_1));
+    is.close();
+    table.close();
+
+    testScannerResultCodes();
+  }
+
+  @Test
+  public void testErrorNotGzipped() throws Exception {
+    Header[] headers = new Header[2];
+    headers[0] = new Header("Accept", Constants.MIMETYPE_BINARY);
+    headers[1] = new Header("Accept-Encoding", "gzip");
+    Response response = client.get("/" + TABLE + "/" + ROW_1 + "/" + COLUMN_2, headers);
+    assertEquals(response.getCode(), 404);
+    String contentEncoding = response.getHeader("Content-Encoding");
+    assertTrue(contentEncoding == null || !contentEncoding.contains("gzip"));
+    response = client.get("/" + TABLE, headers);
+    assertEquals(response.getCode(), 405);
+    contentEncoding = response.getHeader("Content-Encoding");
+    assertTrue(contentEncoding == null || !contentEncoding.contains("gzip"));
+  }
+
+  void testScannerResultCodes() throws Exception {
+    Header[] headers = new Header[3];
+    headers[0] = new Header("Content-Type", Constants.MIMETYPE_XML);
+    headers[1] = new Header("Accept", Constants.MIMETYPE_JSON);
+    headers[2] = new Header("Accept-Encoding", "gzip");
+    Response response = client.post("/" + TABLE + "/scanner", headers,
+        "<Scanner/>".getBytes());
+    assertEquals(response.getCode(), 201);
+    String scannerUrl = response.getLocation();
+    assertNotNull(scannerUrl);
+    response = client.get(scannerUrl);
+    assertEquals(response.getCode(), 200);
+    response = client.get(scannerUrl);
+    assertEquals(response.getCode(), 204);
+  }
+
+}
+

http://git-wip-us.apache.org/repos/asf/hbase/blob/876617bd/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestMultiRowResource.java
----------------------------------------------------------------------
diff --git a/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestMultiRowResource.java b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestMultiRowResource.java
new file mode 100644
index 0000000..0c999b8
--- /dev/null
+++ b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestMultiRowResource.java
@@ -0,0 +1,181 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.rest;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.*;
+import org.apache.hadoop.hbase.client.HBaseAdmin;
+import org.apache.hadoop.hbase.rest.client.Client;
+import org.apache.hadoop.hbase.rest.client.Cluster;
+import org.apache.hadoop.hbase.rest.client.Response;
+import org.apache.hadoop.hbase.rest.model.CellModel;
+import org.apache.hadoop.hbase.rest.model.CellSetModel;
+import org.apache.hadoop.hbase.rest.model.RowModel;
+import org.apache.hadoop.hbase.rest.provider.JacksonProvider;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import javax.ws.rs.core.MediaType;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+
+
+@Category(MediumTests.class)
+public class TestMultiRowResource {
+
+  private static final String TABLE = "TestRowResource";
+  private static final String CFA = "a";
+  private static final String CFB = "b";
+  private static final String COLUMN_1 = CFA + ":1";
+  private static final String COLUMN_2 = CFB + ":2";
+  private static final String ROW_1 = "testrow5";
+  private static final String VALUE_1 = "testvalue5";
+  private static final String ROW_2 = "testrow6";
+  private static final String VALUE_2 = "testvalue6";
+
+
+  private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
+  private static final HBaseRESTTestingUtility REST_TEST_UTIL = new HBaseRESTTestingUtility();
+
+  private static Client client;
+  private static JAXBContext context;
+  private static Marshaller marshaller;
+  private static Unmarshaller unmarshaller;
+  private static Configuration conf;
+
+
+  @BeforeClass
+  public static void setUpBeforeClass() throws Exception {
+    conf = TEST_UTIL.getConfiguration();
+    TEST_UTIL.startMiniCluster();
+    REST_TEST_UTIL.startServletContainer(conf);
+    context = JAXBContext.newInstance(
+            CellModel.class,
+            CellSetModel.class,
+            RowModel.class);
+    marshaller = context.createMarshaller();
+    unmarshaller = context.createUnmarshaller();
+    client = new Client(new Cluster().add("localhost", REST_TEST_UTIL.getServletPort()));
+    HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
+    if (admin.tableExists(TABLE)) {
+      return;
+    }
+    HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(TABLE));
+    htd.addFamily(new HColumnDescriptor(CFA));
+    htd.addFamily(new HColumnDescriptor(CFB));
+    admin.createTable(htd);
+  }
+
+  @AfterClass
+  public static void tearDownAfterClass() throws Exception {
+    REST_TEST_UTIL.shutdownServletContainer();
+    TEST_UTIL.shutdownMiniCluster();
+  }
+
+
+  @Test
+  public void testMultiCellGetJSON() throws IOException, JAXBException {
+    String row_5_url = "/" + TABLE + "/" + ROW_1 + "/" + COLUMN_1;
+    String row_6_url = "/" + TABLE + "/" + ROW_2 + "/" + COLUMN_2;
+
+
+    StringBuilder path = new StringBuilder();
+    path.append("/");
+    path.append(TABLE);
+    path.append("/multiget/?row=");
+    path.append(ROW_1);
+    path.append("&row=");
+    path.append(ROW_2);
+
+    client.post(row_5_url, Constants.MIMETYPE_BINARY, Bytes.toBytes(VALUE_1));
+    client.post(row_6_url, Constants.MIMETYPE_BINARY, Bytes.toBytes(VALUE_2));
+
+
+    Response response = client.get(path.toString(), Constants.MIMETYPE_JSON);
+    assertEquals(response.getCode(), 200);
+    assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
+
+    client.delete(row_5_url);
+    client.delete(row_6_url);
+
+  }
+
+  @Test
+  public void testMultiCellGetXML() throws IOException, JAXBException {
+    String row_5_url = "/" + TABLE + "/" + ROW_1 + "/" + COLUMN_1;
+    String row_6_url = "/" + TABLE + "/" + ROW_2 + "/" + COLUMN_2;
+
+
+    StringBuilder path = new StringBuilder();
+    path.append("/");
+    path.append(TABLE);
+    path.append("/multiget/?row=");
+    path.append(ROW_1);
+    path.append("&row=");
+    path.append(ROW_2);
+
+    client.post(row_5_url, Constants.MIMETYPE_BINARY, Bytes.toBytes(VALUE_1));
+    client.post(row_6_url, Constants.MIMETYPE_BINARY, Bytes.toBytes(VALUE_2));
+
+
+    Response response = client.get(path.toString(), Constants.MIMETYPE_XML);
+    assertEquals(response.getCode(), 200);
+    assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type"));
+
+    client.delete(row_5_url);
+    client.delete(row_6_url);
+
+  }
+
+  @Test
+  public void testMultiCellGetJSONNotFound() throws IOException, JAXBException {
+    String row_5_url = "/" + TABLE + "/" + ROW_1 + "/" + COLUMN_1;
+
+    StringBuilder path = new StringBuilder();
+    path.append("/");
+    path.append(TABLE);
+    path.append("/multiget/?row=");
+    path.append(ROW_1);
+    path.append("&row=");
+    path.append(ROW_2);
+
+    client.post(row_5_url, Constants.MIMETYPE_BINARY, Bytes.toBytes(VALUE_1));
+    Response response = client.get(path.toString(), Constants.MIMETYPE_JSON);
+    assertEquals(response.getCode(), 200);
+    ObjectMapper mapper = new JacksonProvider().locateMapper(CellSetModel.class,
+      MediaType.APPLICATION_JSON_TYPE);
+    CellSetModel cellSet = (CellSetModel) mapper.readValue(response.getBody(), CellSetModel.class);
+    assertEquals(1, cellSet.getRows().size());
+    assertEquals(ROW_1, Bytes.toString(cellSet.getRows().get(0).getKey()));
+    assertEquals(VALUE_1, Bytes.toString(cellSet.getRows().get(0).getCells().get(0).getValue()));
+    client.delete(row_5_url);
+  }
+
+}
+

http://git-wip-us.apache.org/repos/asf/hbase/blob/876617bd/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestResourceFilter.java
----------------------------------------------------------------------
diff --git a/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestResourceFilter.java b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestResourceFilter.java
new file mode 100644
index 0000000..70d425c
--- /dev/null
+++ b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestResourceFilter.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.rest;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.MediumTests;
+import org.apache.hadoop.hbase.rest.client.Client;
+import org.apache.hadoop.hbase.rest.client.Cluster;
+import org.apache.hadoop.hbase.rest.client.Response;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category(MediumTests.class)
+public class TestResourceFilter {
+
+  private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
+  private static final HBaseRESTTestingUtility REST_TEST_UTIL =
+    new HBaseRESTTestingUtility();
+  private static Client client;
+
+  @BeforeClass
+  public static void setUpBeforeClass() throws Exception {
+    TEST_UTIL.getConfiguration().set(Constants.FILTER_CLASSES, DummyFilter.class.getName());
+    TEST_UTIL.startMiniCluster();
+    REST_TEST_UTIL.startServletContainer(TEST_UTIL.getConfiguration());
+    client = new Client(new Cluster().add("localhost",
+      REST_TEST_UTIL.getServletPort()));
+  }
+
+  @AfterClass
+  public static void tearDownAfterClass() throws Exception {
+    REST_TEST_UTIL.shutdownServletContainer();
+    TEST_UTIL.shutdownMiniCluster();
+  }
+
+  @Test
+  public void testFilter() throws Exception {
+    String path = "/status/cluster";
+    Response response = client.get(path);
+    assertEquals(404, response.getCode());
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/876617bd/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestScannerResource.java
----------------------------------------------------------------------
diff --git a/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestScannerResource.java b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestScannerResource.java
new file mode 100644
index 0000000..84aa994
--- /dev/null
+++ b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestScannerResource.java
@@ -0,0 +1,354 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.rest;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Iterator;
+import java.util.Random;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+
+import org.apache.commons.httpclient.Header;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.*;
+import org.apache.hadoop.hbase.client.HBaseAdmin;
+import org.apache.hadoop.hbase.client.HTable;
+import org.apache.hadoop.hbase.client.Put;
+import org.apache.hadoop.hbase.client.Durability;
+import org.apache.hadoop.hbase.rest.client.Client;
+import org.apache.hadoop.hbase.rest.client.Cluster;
+import org.apache.hadoop.hbase.rest.client.Response;
+import org.apache.hadoop.hbase.rest.model.CellModel;
+import org.apache.hadoop.hbase.rest.model.CellSetModel;
+import org.apache.hadoop.hbase.rest.model.RowModel;
+import org.apache.hadoop.hbase.rest.model.ScannerModel;
+import org.apache.hadoop.hbase.util.Bytes;
+
+import static org.junit.Assert.*;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category(MediumTests.class)
+public class TestScannerResource {
+  private static final String TABLE = "TestScannerResource";
+  private static final String NONEXISTENT_TABLE = "ThisTableDoesNotExist";
+  private static final String CFA = "a";
+  private static final String CFB = "b";
+  private static final String COLUMN_1 = CFA + ":1";
+  private static final String COLUMN_2 = CFB + ":2";
+
+  private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
+  private static final HBaseRESTTestingUtility REST_TEST_UTIL = 
+    new HBaseRESTTestingUtility();
+  private static Client client;
+  private static JAXBContext context;
+  private static Marshaller marshaller;
+  private static Unmarshaller unmarshaller;
+  private static int expectedRows1;
+  private static int expectedRows2;
+  private static Configuration conf;
+
+  static int insertData(Configuration conf, String tableName, String column, double prob)
+      throws IOException {
+    Random rng = new Random();
+    int count = 0;
+    HTable table = new HTable(conf, tableName);
+    byte[] k = new byte[3];
+    byte [][] famAndQf = KeyValue.parseColumn(Bytes.toBytes(column));
+    for (byte b1 = 'a'; b1 < 'z'; b1++) {
+      for (byte b2 = 'a'; b2 < 'z'; b2++) {
+        for (byte b3 = 'a'; b3 < 'z'; b3++) {
+          if (rng.nextDouble() < prob) {
+            k[0] = b1;
+            k[1] = b2;
+            k[2] = b3;
+            Put put = new Put(k);
+            put.setDurability(Durability.SKIP_WAL);
+            put.add(famAndQf[0], famAndQf[1], k);
+            table.put(put);
+            count++;
+          }
+        }
+      }
+    }
+    table.flushCommits();
+    table.close();
+    return count;
+  }
+
+  static int countCellSet(CellSetModel model) {
+    int count = 0;
+    Iterator<RowModel> rows = model.getRows().iterator();
+    while (rows.hasNext()) {
+      RowModel row = rows.next();
+      Iterator<CellModel> cells = row.getCells().iterator();
+      while (cells.hasNext()) {
+        cells.next();
+        count++;
+      }
+    }
+    return count;
+  }
+
+  private static int fullTableScan(ScannerModel model) throws IOException {
+    model.setBatch(100);
+    Response response = client.put("/" + TABLE + "/scanner",
+      Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput());
+    assertEquals(response.getCode(), 201);
+    String scannerURI = response.getLocation();
+    assertNotNull(scannerURI);
+    int count = 0;
+    while (true) {
+      response = client.get(scannerURI, Constants.MIMETYPE_PROTOBUF);
+      assertTrue(response.getCode() == 200 || response.getCode() == 204);
+      if (response.getCode() == 200) {
+        assertEquals(Constants.MIMETYPE_PROTOBUF, response.getHeader("content-type"));
+        CellSetModel cellSet = new CellSetModel();
+        cellSet.getObjectFromMessage(response.getBody());
+        Iterator<RowModel> rows = cellSet.getRows().iterator();
+        while (rows.hasNext()) {
+          RowModel row = rows.next();
+          Iterator<CellModel> cells = row.getCells().iterator();
+          while (cells.hasNext()) {
+            cells.next();
+            count++;
+          }
+        }
+      } else {
+        break;
+      }
+    }
+    // delete the scanner
+    response = client.delete(scannerURI);
+    assertEquals(response.getCode(), 200);
+    return count;
+  }
+
+  @BeforeClass
+  public static void setUpBeforeClass() throws Exception {
+    conf = TEST_UTIL.getConfiguration();
+    TEST_UTIL.startMiniCluster();
+    REST_TEST_UTIL.startServletContainer(conf);
+    client = new Client(new Cluster().add("localhost",
+      REST_TEST_UTIL.getServletPort()));
+    context = JAXBContext.newInstance(
+      CellModel.class,
+      CellSetModel.class,
+      RowModel.class,
+      ScannerModel.class);
+    marshaller = context.createMarshaller();
+    unmarshaller = context.createUnmarshaller();
+    HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
+    if (admin.tableExists(TABLE)) {
+      return;
+    }
+    HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(TABLE));
+    htd.addFamily(new HColumnDescriptor(CFA));
+    htd.addFamily(new HColumnDescriptor(CFB));
+    admin.createTable(htd);
+    expectedRows1 = insertData(TEST_UTIL.getConfiguration(), TABLE, COLUMN_1, 1.0);
+    expectedRows2 = insertData(TEST_UTIL.getConfiguration(), TABLE, COLUMN_2, 0.5);
+  }
+
+  @AfterClass
+  public static void tearDownAfterClass() throws Exception {
+    REST_TEST_UTIL.shutdownServletContainer();
+    TEST_UTIL.shutdownMiniCluster();
+  }
+
+  @Test
+  public void testSimpleScannerXML() throws IOException, JAXBException {
+    final int BATCH_SIZE = 5;
+    // new scanner
+    ScannerModel model = new ScannerModel();
+    model.setBatch(BATCH_SIZE);
+    model.addColumn(Bytes.toBytes(COLUMN_1));
+    StringWriter writer = new StringWriter();
+    marshaller.marshal(model, writer);
+    byte[] body = Bytes.toBytes(writer.toString());
+
+    // test put operation is forbidden in read-only mode
+    conf.set("hbase.rest.readonly", "true");
+    Response response = client.put("/" + TABLE + "/scanner",
+      Constants.MIMETYPE_XML, body);
+    assertEquals(response.getCode(), 403);
+    String scannerURI = response.getLocation();
+    assertNull(scannerURI);
+
+    // recall previous put operation with read-only off
+    conf.set("hbase.rest.readonly", "false");
+    response = client.put("/" + TABLE + "/scanner", Constants.MIMETYPE_XML,
+      body);
+    assertEquals(response.getCode(), 201);
+    scannerURI = response.getLocation();
+    assertNotNull(scannerURI);
+
+    // get a cell set
+    response = client.get(scannerURI, Constants.MIMETYPE_XML);
+    assertEquals(response.getCode(), 200);
+    assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type"));
+    CellSetModel cellSet = (CellSetModel)
+      unmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
+    // confirm batch size conformance
+    assertEquals(countCellSet(cellSet), BATCH_SIZE);
+
+    // test delete scanner operation is forbidden in read-only mode
+    conf.set("hbase.rest.readonly", "true");
+    response = client.delete(scannerURI);
+    assertEquals(response.getCode(), 403);
+
+    // recall previous delete scanner operation with read-only off
+    conf.set("hbase.rest.readonly", "false");
+    response = client.delete(scannerURI);
+    assertEquals(response.getCode(), 200);
+  }
+
+  @Test
+  public void testSimpleScannerPB() throws IOException {
+    final int BATCH_SIZE = 10;
+    // new scanner
+    ScannerModel model = new ScannerModel();
+    model.setBatch(BATCH_SIZE);
+    model.addColumn(Bytes.toBytes(COLUMN_1));
+
+    // test put operation is forbidden in read-only mode
+    conf.set("hbase.rest.readonly", "true");
+    Response response = client.put("/" + TABLE + "/scanner",
+      Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput());
+    assertEquals(response.getCode(), 403);
+    String scannerURI = response.getLocation();
+    assertNull(scannerURI);
+
+    // recall previous put operation with read-only off
+    conf.set("hbase.rest.readonly", "false");
+    response = client.put("/" + TABLE + "/scanner",
+      Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput());
+    assertEquals(response.getCode(), 201);
+    scannerURI = response.getLocation();
+    assertNotNull(scannerURI);
+
+    // get a cell set
+    response = client.get(scannerURI, Constants.MIMETYPE_PROTOBUF);
+    assertEquals(response.getCode(), 200);
+    assertEquals(Constants.MIMETYPE_PROTOBUF, response.getHeader("content-type"));
+    CellSetModel cellSet = new CellSetModel();
+    cellSet.getObjectFromMessage(response.getBody());
+    // confirm batch size conformance
+    assertEquals(countCellSet(cellSet), BATCH_SIZE);
+
+    // test delete scanner operation is forbidden in read-only mode
+    conf.set("hbase.rest.readonly", "true");
+    response = client.delete(scannerURI);
+    assertEquals(response.getCode(), 403);
+
+    // recall previous delete scanner operation with read-only off
+    conf.set("hbase.rest.readonly", "false");
+    response = client.delete(scannerURI);
+    assertEquals(response.getCode(), 200);
+  }
+
+  @Test
+  public void testSimpleScannerBinary() throws IOException {
+    // new scanner
+    ScannerModel model = new ScannerModel();
+    model.setBatch(1);
+    model.addColumn(Bytes.toBytes(COLUMN_1));
+
+    // test put operation is forbidden in read-only mode
+    conf.set("hbase.rest.readonly", "true");
+    Response response = client.put("/" + TABLE + "/scanner",
+      Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput());
+    assertEquals(response.getCode(), 403);
+    String scannerURI = response.getLocation();
+    assertNull(scannerURI);
+
+    // recall previous put operation with read-only off
+    conf.set("hbase.rest.readonly", "false");
+    response = client.put("/" + TABLE + "/scanner",
+      Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput());
+    assertEquals(response.getCode(), 201);
+    scannerURI = response.getLocation();
+    assertNotNull(scannerURI);
+
+    // get a cell
+    response = client.get(scannerURI, Constants.MIMETYPE_BINARY);
+    assertEquals(response.getCode(), 200);
+    assertEquals(Constants.MIMETYPE_BINARY, response.getHeader("content-type"));
+    // verify that data was returned
+    assertTrue(response.getBody().length > 0);
+    // verify that the expected X-headers are present
+    boolean foundRowHeader = false, foundColumnHeader = false,
+      foundTimestampHeader = false;
+    for (Header header: response.getHeaders()) {
+      if (header.getName().equals("X-Row")) {
+        foundRowHeader = true;
+      } else if (header.getName().equals("X-Column")) {
+        foundColumnHeader = true;
+      } else if (header.getName().equals("X-Timestamp")) {
+        foundTimestampHeader = true;
+      }
+    }
+    assertTrue(foundRowHeader);
+    assertTrue(foundColumnHeader);
+    assertTrue(foundTimestampHeader);
+
+    // test delete scanner operation is forbidden in read-only mode
+    conf.set("hbase.rest.readonly", "true");
+    response = client.delete(scannerURI);
+    assertEquals(response.getCode(), 403);
+
+    // recall previous delete scanner operation with read-only off
+    conf.set("hbase.rest.readonly", "false");
+    response = client.delete(scannerURI);
+    assertEquals(response.getCode(), 200);
+  }
+
+  @Test
+  public void testFullTableScan() throws IOException {
+    ScannerModel model = new ScannerModel();
+    model.addColumn(Bytes.toBytes(COLUMN_1));
+    assertEquals(fullTableScan(model), expectedRows1);
+
+    model = new ScannerModel();
+    model.addColumn(Bytes.toBytes(COLUMN_2));
+    assertEquals(fullTableScan(model), expectedRows2);
+  }
+
+  @Test
+  public void testTableDoesNotExist() throws IOException, JAXBException {
+    ScannerModel model = new ScannerModel();
+    StringWriter writer = new StringWriter();
+    marshaller.marshal(model, writer);
+    byte[] body = Bytes.toBytes(writer.toString());
+    Response response = client.put("/" + NONEXISTENT_TABLE +
+      "/scanner", Constants.MIMETYPE_XML, body);
+    assertEquals(response.getCode(), 404);
+  }
+
+}
+


Mime
View raw message