Return-Path: X-Original-To: apmail-hbase-commits-archive@www.apache.org Delivered-To: apmail-hbase-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id D61F81768C for ; Fri, 10 Oct 2014 16:53:07 +0000 (UTC) Received: (qmail 47641 invoked by uid 500); 10 Oct 2014 16:53:06 -0000 Delivered-To: apmail-hbase-commits-archive@hbase.apache.org Received: (qmail 47316 invoked by uid 500); 10 Oct 2014 16:53:06 -0000 Mailing-List: contact commits-help@hbase.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@hbase.apache.org Delivered-To: mailing list commits@hbase.apache.org Received: (qmail 46402 invoked by uid 99); 10 Oct 2014 16:53:05 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 10 Oct 2014 16:53:05 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 93FA63DD04; Fri, 10 Oct 2014 16:53:05 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: eclark@apache.org To: commits@hbase.apache.org Date: Fri, 10 Oct 2014 16:53:26 -0000 Message-Id: <9ec81cc62bcb4e06afcbe21823f96791@git.apache.org> In-Reply-To: <6cd52331de444f90bec6b2da9a96accf@git.apache.org> References: <6cd52331de444f90bec6b2da9a96accf@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [23/38] HBASE-12197 Move rest to it's on module http://git-wip-us.apache.org/repos/asf/hbase/blob/052a6f07/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 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 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 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/052a6f07/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..d3d6171 --- /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.Admin; +import org.apache.hadoop.hbase.client.Get; +import org.apache.hadoop.hbase.client.HTable; +import org.apache.hadoop.hbase.client.Result; +import org.apache.hadoop.hbase.client.Table; +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.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 TableName TABLE = TableName.valueOf("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())); + Admin admin = TEST_UTIL.getHBaseAdmin(); + if (admin.tableExists(TABLE)) { + return; + } + HTableDescriptor htd = new HTableDescriptor(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); + + Table 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, + "".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/052a6f07/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..64dc02f --- /dev/null +++ b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestMultiRowResource.java @@ -0,0 +1,182 @@ +/** + * + * 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.Admin; +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 TableName TABLE = TableName.valueOf("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())); + Admin admin = TEST_UTIL.getHBaseAdmin(); + if (admin.tableExists(TABLE)) { + return; + } + HTableDescriptor htd = new HTableDescriptor(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/052a6f07/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/052a6f07/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..e3ed916 --- /dev/null +++ b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestScannerResource.java @@ -0,0 +1,355 @@ +/* + * + * 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.Admin; +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.client.Table; +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 TableName TABLE = TableName.valueOf("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, TableName tableName, String column, double prob) + throws IOException { + Random rng = new Random(); + int count = 0; + Table 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 rows = model.getRows().iterator(); + while (rows.hasNext()) { + RowModel row = rows.next(); + Iterator 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 rows = cellSet.getRows().iterator(); + while (rows.hasNext()) { + RowModel row = rows.next(); + Iterator 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(); + Admin admin = TEST_UTIL.getHBaseAdmin(); + if (admin.tableExists(TABLE)) { + return; + } + HTableDescriptor htd = new HTableDescriptor(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); + } + +} +