diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/MultiRowResource.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/MultiRowResource.java index 2505e7e..82fee52 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/MultiRowResource.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/MultiRowResource.java @@ -27,6 +27,8 @@ import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.CellUtil; @@ -36,6 +38,7 @@ import org.apache.hadoop.hbase.rest.model.RowModel; @InterfaceAudience.Private public class MultiRowResource extends ResourceBase { + private static final Log LOG = LogFactory.getLog(MultiRowResource.class); public static final String ROW_KEYS_PARAM_NAME = "row"; TableResource tableResource; @@ -59,8 +62,7 @@ public class MultiRowResource extends ResourceBase { } @GET - @Produces({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF, - MIMETYPE_PROTOBUF_IETF}) + @Produces({ MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF, MIMETYPE_PROTOBUF_IETF }) public Response get(final @Context UriInfo uriInfo) { MultivaluedMap params = uriInfo.getQueryParameters(); @@ -75,32 +77,33 @@ public class MultiRowResource extends ResourceBase { } ResultGenerator generator = - ResultGenerator.fromRowSpec(this.tableResource.getName(), rowSpec, null); - if (!generator.hasNext()) { - return Response.status(Response.Status.NOT_FOUND) - .type(MIMETYPE_TEXT).entity("Not found" + CRLF) - .build(); - } - + ResultGenerator.fromRowSpec(this.tableResource.getName(), rowSpec, null); Cell value = null; RowModel rowModel = new RowModel(rk); - - while ((value = generator.next()) != null) { - rowModel.addCell(new CellModel(CellUtil.cloneFamily(value), - CellUtil.cloneQualifier(value), - value.getTimestamp(), CellUtil.cloneValue(value))); + if (generator.hasNext()) { + while ((value = generator.next()) != null) { + rowModel.addCell(new CellModel(CellUtil.cloneFamily(value), CellUtil + .cloneQualifier(value), value.getTimestamp(), CellUtil.cloneValue(value))); + } + model.addRow(rowModel); + } else { + LOG.trace("The row : " + rk + " not found in the table."); } + } - model.addRow(rowModel); + if (model.getRows().size() == 0) { + //If no rows found. + servlet.getMetrics().incrementFailedGetRequests(1); + return Response.status(Response.Status.NOT_FOUND) + .type(MIMETYPE_TEXT).entity("No rows found." + CRLF) + .build(); + } else { + servlet.getMetrics().incrementSucessfulGetRequests(1); + return Response.ok(model).build(); } - servlet.getMetrics().incrementSucessfulGetRequests(1); - return Response.ok(model).build(); - } catch (IOException e) { + } catch (Exception e) { servlet.getMetrics().incrementFailedGetRequests(1); - return Response.status(Response.Status.SERVICE_UNAVAILABLE) - .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF) - .build(); + return processException(e); } - } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/ResourceBase.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/ResourceBase.java index c81b1eb..fd46b31 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/ResourceBase.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/ResourceBase.java @@ -21,14 +21,73 @@ package org.apache.hadoop.hbase.rest; import java.io.IOException; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; + import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.hbase.TableNotFoundException; +import org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException; +import org.apache.hadoop.hbase.regionserver.NoSuchColumnFamilyException; +import org.apache.hadoop.util.StringUtils; @InterfaceAudience.Private public class ResourceBase implements Constants { RESTServlet servlet; + Class accessDeniedClazz; public ResourceBase() throws IOException { servlet = RESTServlet.getInstance(); + try { + accessDeniedClazz = Class.forName("org.apache.hadoop.hbase.security.AccessDeniedException"); + } catch (ClassNotFoundException e) { + } + } + + protected Response processException(Throwable exp) { + Throwable curr = exp; + if(accessDeniedClazz != null) { + //some access denied exceptions are buried + while (curr != null) { + if(accessDeniedClazz.isAssignableFrom(curr.getClass())) { + throw new SecurityException("Unauthorized" + CRLF + + StringUtils.stringifyException(exp) + CRLF); + } + curr = curr.getCause(); + } + } + //TableNotFound may also be buried one level deep + if (exp instanceof TableNotFoundException || + exp.getCause() instanceof TableNotFoundException) { + throw new WebApplicationException( + Response.status(Response.Status.NOT_FOUND) + .type(MIMETYPE_TEXT).entity("Not found" + CRLF + + StringUtils.stringifyException(exp) + CRLF) + .build()); + } + if (exp instanceof NoSuchColumnFamilyException){ + throw new WebApplicationException( + Response.status(Response.Status.NOT_FOUND) + .type(MIMETYPE_TEXT).entity("Not found" + CRLF + + StringUtils.stringifyException(exp) + CRLF) + .build()); + } + if (exp instanceof RuntimeException) { + throw new WebApplicationException( + Response.status(Response.Status.BAD_REQUEST) + .type(MIMETYPE_TEXT).entity("Bad request" + CRLF + + StringUtils.stringifyException(exp) + CRLF) + .build()); + } + if (exp instanceof RetriesExhaustedWithDetailsException) { + RetriesExhaustedWithDetailsException retryException = + (RetriesExhaustedWithDetailsException) exp; + processException(retryException.getCause(0)); + } + throw new WebApplicationException( + Response.status(Response.Status.SERVICE_UNAVAILABLE) + .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF + + StringUtils.stringifyException(exp) + CRLF) + .build()); } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RootResource.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RootResource.java index 2d45643..0bae886 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RootResource.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RootResource.java @@ -81,11 +81,9 @@ public class RootResource extends ResourceBase { response.cacheControl(cacheControl); servlet.getMetrics().incrementSucessfulGetRequests(1); return response.build(); - } catch (IOException e) { + } catch (Exception e) { servlet.getMetrics().incrementFailedGetRequests(1); - return Response.status(Response.Status.SERVICE_UNAVAILABLE) - .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF) - .build(); + return processException(e); } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RowResource.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RowResource.java index cf8c25f..8492c4a 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RowResource.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RowResource.java @@ -35,6 +35,7 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.UriInfo; +import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.classification.InterfaceAudience; @@ -42,7 +43,6 @@ import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.CellUtil; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.KeyValue; -import org.apache.hadoop.hbase.TableNotFoundException; import org.apache.hadoop.hbase.client.Delete; import org.apache.hadoop.hbase.client.HTableInterface; import org.apache.hadoop.hbase.client.Put; @@ -118,21 +118,9 @@ public class RowResource extends ResourceBase { model.addRow(rowModel); servlet.getMetrics().incrementSucessfulGetRequests(1); return Response.ok(model).build(); - } catch (RuntimeException e) { - servlet.getMetrics().incrementFailedGetRequests(1); - if (e.getCause() instanceof TableNotFoundException) { - return Response.status(Response.Status.NOT_FOUND) - .type(MIMETYPE_TEXT).entity("Not found" + CRLF) - .build(); - } - return Response.status(Response.Status.BAD_REQUEST) - .type(MIMETYPE_TEXT).entity("Bad request" + CRLF) - .build(); } catch (Exception e) { - servlet.getMetrics().incrementFailedGetRequests(1); - return Response.status(Response.Status.SERVICE_UNAVAILABLE) - .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF) - .build(); + servlet.getMetrics().incrementFailedPutRequests(1); + return processException(e); } } @@ -146,9 +134,9 @@ public class RowResource extends ResourceBase { // doesn't make sense to use a non specific coordinate as this can only // return a single cell if (!rowspec.hasColumns() || rowspec.getColumns().length > 1) { - return Response.status(Response.Status.BAD_REQUEST) - .type(MIMETYPE_TEXT).entity("Bad request" + CRLF) - .build(); + servlet.getMetrics().incrementFailedGetRequests(1); + return Response.status(Response.Status.BAD_REQUEST).type(MIMETYPE_TEXT) + .entity("Bad request: Either 0 or more than 1 columns specified." + CRLF).build(); } try { ResultGenerator generator = @@ -164,17 +152,16 @@ public class RowResource extends ResourceBase { response.header("X-Timestamp", value.getTimestamp()); servlet.getMetrics().incrementSucessfulGetRequests(1); return response.build(); - } catch (IOException e) { + } catch (Exception e) { servlet.getMetrics().incrementFailedGetRequests(1); - return Response.status(Response.Status.SERVICE_UNAVAILABLE) - .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF) - .build(); + return processException(e); } } Response update(final CellSetModel model, final boolean replace) { servlet.getMetrics().incrementRequests(1); if (servlet.isReadOnly()) { + servlet.getMetrics().incrementFailedPutRequests(1); return Response.status(Response.Status.FORBIDDEN) .type(MIMETYPE_TEXT).entity("Forbidden" + CRLF) .build(); @@ -200,8 +187,9 @@ public class RowResource extends ResourceBase { key = rowspec.getRow(); } if (key == null) { + servlet.getMetrics().incrementFailedPutRequests(1); return Response.status(Response.Status.BAD_REQUEST) - .type(MIMETYPE_TEXT).entity("Bad request" + CRLF) + .type(MIMETYPE_TEXT).entity("Bad request: Row key not specified." + CRLF) .build(); } Put put = new Put(key); @@ -214,8 +202,9 @@ public class RowResource extends ResourceBase { col = null; } if (col == null) { + servlet.getMetrics().incrementFailedPutRequests(1); return Response.status(Response.Status.BAD_REQUEST) - .type(MIMETYPE_TEXT).entity("Bad request" + CRLF) + .type(MIMETYPE_TEXT).entity("Bad request: Column found to be null." + CRLF) .build(); } byte [][] parts = KeyValue.parseColumn(col); @@ -237,11 +226,9 @@ public class RowResource extends ResourceBase { ResponseBuilder response = Response.ok(); servlet.getMetrics().incrementSucessfulPutRequests(1); return response.build(); - } catch (IOException e) { + } catch (Exception e) { servlet.getMetrics().incrementFailedPutRequests(1); - return Response.status(Response.Status.SERVICE_UNAVAILABLE) - .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF) - .build(); + return processException(e); } finally { if (table != null) try { table.close(); @@ -256,6 +243,7 @@ public class RowResource extends ResourceBase { final boolean replace) { servlet.getMetrics().incrementRequests(1); if (servlet.isReadOnly()) { + servlet.getMetrics().incrementFailedPutRequests(1); return Response.status(Response.Status.FORBIDDEN) .type(MIMETYPE_TEXT).entity("Forbidden" + CRLF) .build(); @@ -282,9 +270,10 @@ public class RowResource extends ResourceBase { timestamp = Long.valueOf(vals.get(0)); } if (column == null) { + servlet.getMetrics().incrementFailedPutRequests(1); return Response.status(Response.Status.BAD_REQUEST) - .type(MIMETYPE_TEXT).entity("Bad request" + CRLF) - .build(); + .type(MIMETYPE_TEXT).entity("Bad request: Column found to be null." + CRLF) + .build(); } Put put = new Put(row); byte parts[][] = KeyValue.parseColumn(column); @@ -301,15 +290,15 @@ public class RowResource extends ResourceBase { } servlet.getMetrics().incrementSucessfulPutRequests(1); return Response.ok().build(); - } catch (IOException e) { + } catch (Exception e) { servlet.getMetrics().incrementFailedPutRequests(1); - return Response.status(Response.Status.SERVICE_UNAVAILABLE) - .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF) - .build(); + return processException(e); } finally { if (table != null) try { table.close(); - } catch (IOException ioe) { } + } catch (IOException ioe) { + LOG.debug(ioe); + } } } @@ -364,6 +353,7 @@ public class RowResource extends ResourceBase { } servlet.getMetrics().incrementRequests(1); if (servlet.isReadOnly()) { + servlet.getMetrics().incrementFailedDeleteRequests(1); return Response.status(Response.Status.FORBIDDEN) .type(MIMETYPE_TEXT).entity("Forbidden" + CRLF) .build(); @@ -406,15 +396,15 @@ public class RowResource extends ResourceBase { if (LOG.isDebugEnabled()) { LOG.debug("DELETE " + delete.toString()); } - } catch (IOException e) { + } catch (Exception e) { servlet.getMetrics().incrementFailedDeleteRequests(1); - return Response.status(Response.Status.SERVICE_UNAVAILABLE) - .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF) - .build(); + return processException(e); } finally { if (table != null) try { table.close(); - } catch (IOException ioe) { } + } catch (IOException ioe) { + LOG.debug(ioe); + } } return Response.ok().build(); } @@ -429,10 +419,11 @@ public class RowResource extends ResourceBase { Response checkAndPut(final CellSetModel model) { HTableInterface table = null; try { + table = servlet.getTable(tableResource.getName()); if (model.getRows().size() != 1) { - return Response.status(Response.Status.BAD_REQUEST) - .type(MIMETYPE_TEXT).entity("Bad request" + CRLF) - .build(); + servlet.getMetrics().incrementFailedPutRequests(1); + return Response.status(Response.Status.BAD_REQUEST).type(MIMETYPE_TEXT) + .entity("Bad request: Number of rows specified is not 1." + CRLF).build(); } RowModel rowModel = model.getRows().get(0); @@ -444,56 +435,68 @@ public class RowResource extends ResourceBase { List cellModels = rowModel.getCells(); int cellModelCount = cellModels.size(); if (key == null || cellModelCount <= 1) { - return Response.status(Response.Status.BAD_REQUEST) - .type(MIMETYPE_TEXT).entity("Bad request" + CRLF) - .build(); + servlet.getMetrics().incrementFailedPutRequests(1); + return Response + .status(Response.Status.BAD_REQUEST) + .type(MIMETYPE_TEXT) + .entity( + "Bad request: Either row key is null or no data found for columns specified." + CRLF) + .build(); } Put put = new Put(key); + boolean retValue; CellModel valueToCheckCell = cellModels.get(cellModelCount - 1); byte[] valueToCheckColumn = valueToCheckCell.getColumn(); byte[][] valueToPutParts = KeyValue.parseColumn(valueToCheckColumn); - if (valueToPutParts.length != 2) { - return Response.status(Response.Status.BAD_REQUEST) - .type(MIMETYPE_TEXT).entity("Bad request" + CRLF) - .build(); - } - CellModel valueToPutCell = null; - for (int i = 0, n = cellModelCount - 1; i < n ; i++) { - if(Bytes.equals(cellModels.get(i).getColumn(), - valueToCheckCell.getColumn())) { - valueToPutCell = cellModels.get(i); - break; + if (valueToPutParts.length == 2 && valueToPutParts[1].length > 0) { + CellModel valueToPutCell = null; + for (int i = 0, n = cellModelCount - 1; i < n ; i++) { + if(Bytes.equals(cellModels.get(i).getColumn(), + valueToCheckCell.getColumn())) { + valueToPutCell = cellModels.get(i); + break; + } } - } - if (null == valueToPutCell) { + if (valueToPutCell == null) { + servlet.getMetrics().incrementFailedPutRequests(1); + return Response.status(Response.Status.BAD_REQUEST).type(MIMETYPE_TEXT) + .entity("Bad request: The column to put and check do not match." + CRLF).build(); + } else { + put.addImmutable(valueToPutParts[0], valueToPutParts[1], valueToPutCell.getTimestamp(), + valueToPutCell.getValue()); + retValue = table.checkAndPut(key, valueToPutParts[0], valueToPutParts[1], + valueToCheckCell.getValue(), put); + } + } else { + servlet.getMetrics().incrementFailedPutRequests(1); return Response.status(Response.Status.BAD_REQUEST) - .type(MIMETYPE_TEXT).entity("Bad request" + CRLF) + .type(MIMETYPE_TEXT).entity("Bad request: Column incorrectly specified." + CRLF) .build(); } - put.addImmutable(valueToPutParts[0], valueToPutParts[1], valueToPutCell - .getTimestamp(), valueToPutCell.getValue()); - table = servlet.getTable(this.tableResource.getName()); - boolean retValue = table.checkAndPut(key, valueToPutParts[0], - valueToPutParts[1], valueToCheckCell.getValue(), put); if (LOG.isDebugEnabled()) { LOG.debug("CHECK-AND-PUT " + put.toString() + ", returns " + retValue); } - table.flushCommits(); - ResponseBuilder response = Response.ok(); if (!retValue) { - response = Response.status(304); + servlet.getMetrics().incrementFailedPutRequests(1); + return Response.status(Response.Status.NOT_MODIFIED) + .type(MIMETYPE_TEXT).entity("Value not Modified" + CRLF) + .build(); } + table.flushCommits(); + ResponseBuilder response = Response.ok(); + servlet.getMetrics().incrementSucessfulPutRequests(1); return response.build(); - } catch (IOException e) { - return Response.status(Response.Status.SERVICE_UNAVAILABLE) - .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF) - .build(); + } catch (Exception e) { + servlet.getMetrics().incrementFailedPutRequests(1); + return processException(e); } finally { if (table != null) try { table.close(); - } catch (IOException ioe) { } + } catch (IOException ioe) { + LOG.debug("Exception received while closing the table", ioe); + } } } @@ -508,7 +511,9 @@ public class RowResource extends ResourceBase { HTableInterface table = null; Delete delete = null; try { + table = servlet.getTable(tableResource.getName()); if (model.getRows().size() != 1) { + servlet.getMetrics().incrementFailedDeleteRequests(1); return Response.status(Response.Status.BAD_REQUEST) .type(MIMETYPE_TEXT).entity("Bad request" + CRLF) .build(); @@ -519,48 +524,64 @@ public class RowResource extends ResourceBase { key = rowspec.getRow(); } if (key == null) { + servlet.getMetrics().incrementFailedDeleteRequests(1); return Response.status(Response.Status.BAD_REQUEST) - .type(MIMETYPE_TEXT).entity("Bad request" + CRLF) + .type(MIMETYPE_TEXT).entity("Bad request: Row key found to be null." + CRLF) .build(); } delete = new Delete(key); + boolean retValue; CellModel valueToDeleteCell = rowModel.getCells().get(0); byte[] valueToDeleteColumn = valueToDeleteCell.getColumn(); if (valueToDeleteColumn == null) { try { valueToDeleteColumn = rowspec.getColumns()[0]; } catch (final ArrayIndexOutOfBoundsException e) { + servlet.getMetrics().incrementFailedDeleteRequests(1); return Response.status(Response.Status.BAD_REQUEST) - .type(MIMETYPE_TEXT).entity("Bad request" + CRLF) + .type(MIMETYPE_TEXT).entity("Bad request: Column not specified for check." + CRLF) .build(); } } byte[][] parts = KeyValue.parseColumn(valueToDeleteColumn); - if (parts.length != 2) { + if (parts.length == 2) { + if (parts[1].length != 0) { + delete.deleteColumns(parts[0], parts[1]); + retValue = table.checkAndDelete(key, parts[0], parts[1], + valueToDeleteCell.getValue(), delete); + } else { + // The case of empty qualifier. + delete.deleteColumns(parts[0], Bytes.toBytes(StringUtils.EMPTY)); + retValue = table.checkAndDelete(key, parts[0], Bytes.toBytes(StringUtils.EMPTY), + valueToDeleteCell.getValue(), delete); + } + } else { + servlet.getMetrics().incrementFailedDeleteRequests(1); return Response.status(Response.Status.BAD_REQUEST) - .type(MIMETYPE_TEXT).entity("Bad request" + CRLF) + .type(MIMETYPE_TEXT).entity("Bad request: Column incorrectly specified." + CRLF) .build(); } delete.deleteColumns(parts[0], parts[1]); - table = servlet.getTable(tableResource.getName()); - boolean retValue = table.checkAndDelete(key, parts[0], parts[1], - valueToDeleteCell.getValue(), delete); if (LOG.isDebugEnabled()) { LOG.debug("CHECK-AND-DELETE " + delete.toString() + ", returns " + retValue); } - table.flushCommits(); - ResponseBuilder response = Response.ok(); + if (!retValue) { - response = Response.status(304); + servlet.getMetrics().incrementFailedDeleteRequests(1); + return Response.status(Response.Status.NOT_MODIFIED) + .type(MIMETYPE_TEXT).entity(" Delete check failed." + CRLF) + .build(); } + table.flushCommits(); + ResponseBuilder response = Response.ok(); + servlet.getMetrics().incrementSucessfulDeleteRequests(1); return response.build(); - } catch (IOException e) { - return Response.status(Response.Status.SERVICE_UNAVAILABLE) - .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF) - .build(); + } catch (Exception e) { + servlet.getMetrics().incrementFailedDeleteRequests(1); + return processException(e); } finally { if (table != null) try { table.close(); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/SchemaResource.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/SchemaResource.java index 6546a78..ae03651 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/SchemaResource.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/SchemaResource.java @@ -96,17 +96,10 @@ public class SchemaResource extends ResourceBase { response.cacheControl(cacheControl); servlet.getMetrics().incrementSucessfulGetRequests(1); return response.build(); - } catch (TableNotFoundException e) { + } catch (Exception e) { servlet.getMetrics().incrementFailedGetRequests(1); - return Response.status(Response.Status.NOT_FOUND) - .type(MIMETYPE_TEXT).entity("Not found" + CRLF) - .build(); - } catch (IOException e) { - servlet.getMetrics().incrementFailedGetRequests(1); - return Response.status(Response.Status.SERVICE_UNAVAILABLE) - .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF) - .build(); - } + return processException(e); + } } private Response replace(final byte[] name, final TableSchemaModel model, @@ -143,10 +136,9 @@ public class SchemaResource extends ResourceBase { .build(); } return Response.created(uriInfo.getAbsolutePath()).build(); - } catch (IOException e) { - return Response.status(Response.Status.SERVICE_UNAVAILABLE) - .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF) - .build(); + } catch (Exception e) { + servlet.getMetrics().incrementFailedPutRequests(1); + return processException(e); } } @@ -181,10 +173,9 @@ public class SchemaResource extends ResourceBase { } servlet.getMetrics().incrementSucessfulPutRequests(1); return Response.ok().build(); - } catch (IOException e) { - return Response.status(Response.Status.SERVICE_UNAVAILABLE) - .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF) - .build(); + } catch (Exception e) { + servlet.getMetrics().incrementFailedPutRequests(1); + return processException(e); } } @@ -198,11 +189,9 @@ public class SchemaResource extends ResourceBase { } else { return update(name, model, uriInfo, admin); } - } catch (IOException e) { + } catch (Exception e) { servlet.getMetrics().incrementFailedPutRequests(1); - return Response.status(Response.Status.SERVICE_UNAVAILABLE) - .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF) - .build(); + return processException(e); } } @@ -255,16 +244,9 @@ public class SchemaResource extends ResourceBase { admin.deleteTable(tableResource.getName()); servlet.getMetrics().incrementSucessfulDeleteRequests(1); return Response.ok().build(); - } catch (TableNotFoundException e) { + } catch (Exception e) { servlet.getMetrics().incrementFailedDeleteRequests(1); - return Response.status(Response.Status.NOT_FOUND) - .type(MIMETYPE_TEXT).entity("Not found" + CRLF) - .build(); - } catch (IOException e) { - servlet.getMetrics().incrementFailedDeleteRequests(1); - return Response.status(Response.Status.SERVICE_UNAVAILABLE) - .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF) - .build(); + return processException(e); } } } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/RowResourceBase.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/RowResourceBase.java new file mode 100644 index 0000000..adebc6b --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/RowResourceBase.java @@ -0,0 +1,481 @@ +/** + * 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 java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.StringWriter; + +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 org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.TableName; +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.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; + +public class RowResourceBase { + + protected static final String TABLE = "TestRowResource"; + protected static final String CFA = "a"; + protected static final String CFB = "b"; + protected static final String COLUMN_1 = CFA + ":1"; + protected static final String COLUMN_2 = CFB + ":2"; + protected static final String COLUMN_3 = CFA + ":"; + protected static final String ROW_1 = "testrow1"; + protected static final String VALUE_1 = "testvalue1"; + protected static final String ROW_2 = "testrow2"; + protected static final String VALUE_2 = "testvalue2"; + protected static final String ROW_3 = "testrow3"; + protected static final String VALUE_3 = "testvalue3"; + protected static final String ROW_4 = "testrow4"; + protected static final String VALUE_4 = "testvalue4"; + + protected static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); + protected static final HBaseRESTTestingUtility REST_TEST_UTIL = + new HBaseRESTTestingUtility(); + protected static Client client; + protected static JAXBContext context; + protected static Marshaller xmlMarshaller; + protected static Unmarshaller xmlUnmarshaller; + protected static Configuration conf; + protected static ObjectMapper jsonMapper; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + conf = TEST_UTIL.getConfiguration(); + TEST_UTIL.startMiniCluster(3); + REST_TEST_UTIL.startServletContainer(conf); + context = JAXBContext.newInstance( + CellModel.class, + CellSetModel.class, + RowModel.class); + xmlMarshaller = context.createMarshaller(); + xmlUnmarshaller = context.createUnmarshaller(); + jsonMapper = new JacksonProvider() + .locateMapper(CellSetModel.class, MediaType.APPLICATION_JSON_TYPE); + 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(); + } + + @Before + public void beforeMethod() throws Exception { + HBaseAdmin admin = TEST_UTIL.getHBaseAdmin(); + if (admin.tableExists(TABLE)) { + TEST_UTIL.deleteTable(Bytes.toBytes(TABLE)); + } + HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(TABLE)); + htd.addFamily(new HColumnDescriptor(CFA)); + htd.addFamily(new HColumnDescriptor(CFB)); + admin.createTable(htd); + } + + @After + public void afterMethod() throws Exception { + HBaseAdmin admin = TEST_UTIL.getHBaseAdmin(); + if (admin.tableExists(TABLE)) { + TEST_UTIL.deleteTable(Bytes.toBytes(TABLE)); + } + } + + static Response putValuePB(String table, String row, String column, + String value) throws IOException { + StringBuilder path = new StringBuilder(); + path.append('/'); + path.append(table); + path.append('/'); + path.append(row); + path.append('/'); + path.append(column); + return putValuePB(path.toString(), table, row, column, value); + } + + static Response putValuePB(String url, String table, String row, + String column, String value) throws IOException { + RowModel rowModel = new RowModel(row); + rowModel.addCell(new CellModel(Bytes.toBytes(column), + Bytes.toBytes(value))); + CellSetModel cellSetModel = new CellSetModel(); + cellSetModel.addRow(rowModel); + Response response = client.put(url, Constants.MIMETYPE_PROTOBUF, + cellSetModel.createProtobufOutput()); + Thread.yield(); + return response; + } + + protected static void checkValueXML(String url, String table, String row, + String column, String value) throws IOException, JAXBException { + Response response = getValueXML(url); + assertEquals(response.getCode(), 200); + assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type")); + CellSetModel cellSet = (CellSetModel) + xmlUnmarshaller.unmarshal(new ByteArrayInputStream(response.getBody())); + RowModel rowModel = cellSet.getRows().get(0); + CellModel cell = rowModel.getCells().get(0); + assertEquals(Bytes.toString(cell.getColumn()), column); + assertEquals(Bytes.toString(cell.getValue()), value); + } + + protected static void checkValueXML(String table, String row, String column, + String value) throws IOException, JAXBException { + Response response = getValueXML(table, row, column); + assertEquals(response.getCode(), 200); + assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type")); + CellSetModel cellSet = (CellSetModel) + xmlUnmarshaller.unmarshal(new ByteArrayInputStream(response.getBody())); + RowModel rowModel = cellSet.getRows().get(0); + CellModel cell = rowModel.getCells().get(0); + assertEquals(Bytes.toString(cell.getColumn()), column); + assertEquals(Bytes.toString(cell.getValue()), value); + } + + protected static Response getValuePB(String url) throws IOException { + Response response = client.get(url, Constants.MIMETYPE_PROTOBUF); + return response; + } + + protected static Response putValueXML(String table, String row, String column, + String value) throws IOException, JAXBException { + StringBuilder path = new StringBuilder(); + path.append('/'); + path.append(table); + path.append('/'); + path.append(row); + path.append('/'); + path.append(column); + return putValueXML(path.toString(), table, row, column, value); + } + + protected static Response putValueXML(String url, String table, String row, + String column, String value) throws IOException, JAXBException { + RowModel rowModel = new RowModel(row); + rowModel.addCell(new CellModel(Bytes.toBytes(column), + Bytes.toBytes(value))); + CellSetModel cellSetModel = new CellSetModel(); + cellSetModel.addRow(rowModel); + StringWriter writer = new StringWriter(); + xmlMarshaller.marshal(cellSetModel, writer); + Response response = client.put(url, Constants.MIMETYPE_XML, + Bytes.toBytes(writer.toString())); + Thread.yield(); + return response; + } + + protected static Response getValuePB(String table, String row, String column) + throws IOException { + StringBuilder path = new StringBuilder(); + path.append('/'); + path.append(table); + path.append('/'); + path.append(row); + path.append('/'); + path.append(column); + return getValuePB(path.toString()); + } + + protected static void checkValuePB(String table, String row, String column, + String value) throws IOException { + Response response = getValuePB(table, row, column); + assertEquals(response.getCode(), 200); + assertEquals(Constants.MIMETYPE_PROTOBUF, response.getHeader("content-type")); + CellSetModel cellSet = new CellSetModel(); + cellSet.getObjectFromMessage(response.getBody()); + RowModel rowModel = cellSet.getRows().get(0); + CellModel cell = rowModel.getCells().get(0); + assertEquals(Bytes.toString(cell.getColumn()), column); + assertEquals(Bytes.toString(cell.getValue()), value); + } + + protected static Response checkAndPutValuePB(String url, String table, + String row, String column, String valueToCheck, String valueToPut) + throws IOException { + RowModel rowModel = new RowModel(row); + rowModel.addCell(new CellModel(Bytes.toBytes(column), + Bytes.toBytes(valueToPut))); + rowModel.addCell(new CellModel(Bytes.toBytes(column), + Bytes.toBytes(valueToCheck))); + CellSetModel cellSetModel = new CellSetModel(); + cellSetModel.addRow(rowModel); + Response response = client.put(url, Constants.MIMETYPE_PROTOBUF, + cellSetModel.createProtobufOutput()); + Thread.yield(); + return response; + } + + protected static Response checkAndPutValuePB(String table, String row, + String column, String valueToCheck, String valueToPut) throws IOException { + StringBuilder path = new StringBuilder(); + path.append('/'); + path.append(table); + path.append('/'); + path.append(row); + path.append("?check=put"); + return checkAndPutValuePB(path.toString(), table, row, column, + valueToCheck, valueToPut); + } + + protected static Response checkAndPutValueXML(String url, String table, + String row, String column, String valueToCheck, String valueToPut) + throws IOException, JAXBException { + RowModel rowModel = new RowModel(row); + rowModel.addCell(new CellModel(Bytes.toBytes(column), + Bytes.toBytes(valueToPut))); + rowModel.addCell(new CellModel(Bytes.toBytes(column), + Bytes.toBytes(valueToCheck))); + CellSetModel cellSetModel = new CellSetModel(); + cellSetModel.addRow(rowModel); + StringWriter writer = new StringWriter(); + xmlMarshaller.marshal(cellSetModel, writer); + Response response = client.put(url, Constants.MIMETYPE_XML, + Bytes.toBytes(writer.toString())); + Thread.yield(); + return response; + } + + protected static Response checkAndPutValueXML(String table, String row, + String column, String valueToCheck, String valueToPut) + throws IOException, JAXBException { + StringBuilder path = new StringBuilder(); + path.append('/'); + path.append(table); + path.append('/'); + path.append(row); + path.append("?check=put"); + return checkAndPutValueXML(path.toString(), table, row, column, + valueToCheck, valueToPut); + } + + protected static Response checkAndDeleteXML(String url, String table, + String row, String column, String valueToCheck) + throws IOException, JAXBException { + RowModel rowModel = new RowModel(row); + rowModel.addCell(new CellModel(Bytes.toBytes(column), + Bytes.toBytes(valueToCheck))); + CellSetModel cellSetModel = new CellSetModel(); + cellSetModel.addRow(rowModel); + StringWriter writer = new StringWriter(); + xmlMarshaller.marshal(cellSetModel, writer); + Response response = client.put(url, Constants.MIMETYPE_XML, + Bytes.toBytes(writer.toString())); + Thread.yield(); + return response; + } + + protected static Response checkAndDeleteXML(String table, String row, + String column, String valueToCheck) throws IOException, JAXBException { + StringBuilder path = new StringBuilder(); + path.append('/'); + path.append(table); + path.append('/'); + path.append(row); + path.append("?check=delete"); + return checkAndDeleteXML(path.toString(), table, row, column, valueToCheck); + } + + protected static Response checkAndDeleteJson(String table, String row, + String column, String valueToCheck) throws IOException, JAXBException { + StringBuilder path = new StringBuilder(); + path.append('/'); + path.append(table); + path.append('/'); + path.append(row); + path.append("?check=delete"); + return checkAndDeleteJson(path.toString(), table, row, column, valueToCheck); + } + + protected static Response checkAndDeleteJson(String url, String table, + String row, String column, String valueToCheck) + throws IOException, JAXBException { + RowModel rowModel = new RowModel(row); + rowModel.addCell(new CellModel(Bytes.toBytes(column), + Bytes.toBytes(valueToCheck))); + CellSetModel cellSetModel = new CellSetModel(); + cellSetModel.addRow(rowModel); + String jsonString = jsonMapper.writeValueAsString(cellSetModel); + Response response = client.put(url, Constants.MIMETYPE_JSON, + Bytes.toBytes(jsonString)); + Thread.yield(); + return response; + } + + protected static Response checkAndDeletePB(String table, String row, + String column, String value) throws IOException { + StringBuilder path = new StringBuilder(); + path.append('/'); + path.append(table); + path.append('/'); + path.append(row); + path.append("?check=delete"); + return checkAndDeleteValuePB(path.toString(), table, row, column, value); + } + + protected static Response checkAndDeleteValuePB(String url, String table, + String row, String column, String valueToCheck) + throws IOException { + RowModel rowModel = new RowModel(row); + rowModel.addCell(new CellModel(Bytes.toBytes(column), Bytes + .toBytes(valueToCheck))); + CellSetModel cellSetModel = new CellSetModel(); + cellSetModel.addRow(rowModel); + Response response = client.put(url, Constants.MIMETYPE_PROTOBUF, + cellSetModel.createProtobufOutput()); + Thread.yield(); + return response; + } + + protected static Response getValueXML(String table, String startRow, + String endRow, String column) throws IOException { + StringBuilder path = new StringBuilder(); + path.append('/'); + path.append(table); + path.append('/'); + path.append(startRow); + path.append(","); + path.append(endRow); + path.append('/'); + path.append(column); + return getValueXML(path.toString()); + } + + protected static Response getValueXML(String url) throws IOException { + Response response = client.get(url, Constants.MIMETYPE_XML); + return response; + } + + protected static Response getValueJson(String url) throws IOException { + Response response = client.get(url, Constants.MIMETYPE_JSON); + return response; + } + + protected static Response deleteValue(String table, String row, String column) + throws IOException { + StringBuilder path = new StringBuilder(); + path.append('/'); + path.append(table); + path.append('/'); + path.append(row); + path.append('/'); + path.append(column); + Response response = client.delete(path.toString()); + Thread.yield(); + return response; + } + + protected static Response getValueXML(String table, String row, String column) + throws IOException { + StringBuilder path = new StringBuilder(); + path.append('/'); + path.append(table); + path.append('/'); + path.append(row); + path.append('/'); + path.append(column); + return getValueXML(path.toString()); + } + + protected static Response deleteRow(String table, String row) + throws IOException { + StringBuilder path = new StringBuilder(); + path.append('/'); + path.append(table); + path.append('/'); + path.append(row); + Response response = client.delete(path.toString()); + Thread.yield(); + return response; + } + + protected static Response getValueJson(String table, String row, + String column) throws IOException { + StringBuilder path = new StringBuilder(); + path.append('/'); + path.append(table); + path.append('/'); + path.append(row); + path.append('/'); + path.append(column); + return getValueJson(path.toString()); + } + + protected static void checkValueJSON(String table, String row, String column, + String value) throws IOException, JAXBException { + Response response = getValueJson(table, row, column); + assertEquals(response.getCode(), 200); + assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type")); + ObjectMapper mapper = new JacksonProvider() + .locateMapper(CellSetModel.class, MediaType.APPLICATION_JSON_TYPE); + CellSetModel cellSet = mapper.readValue(response.getBody(), CellSetModel.class); + RowModel rowModel = cellSet.getRows().get(0); + CellModel cell = rowModel.getCells().get(0); + assertEquals(Bytes.toString(cell.getColumn()), column); + assertEquals(Bytes.toString(cell.getValue()), value); + } + + protected static Response putValueJson(String table, String row, String column, + String value) throws IOException, JAXBException { + StringBuilder path = new StringBuilder(); + path.append('/'); + path.append(table); + path.append('/'); + path.append(row); + path.append('/'); + path.append(column); + return putValueJson(path.toString(), table, row, column, value); + } + + protected static Response putValueJson(String url, String table, String row, String column, + String value) throws IOException, JAXBException { + RowModel rowModel = new RowModel(row); + rowModel.addCell(new CellModel(Bytes.toBytes(column), + Bytes.toBytes(value))); + CellSetModel cellSetModel = new CellSetModel(); + cellSetModel.addRow(rowModel); + String jsonString = jsonMapper.writeValueAsString(cellSetModel); + Response response = client.put(url, Constants.MIMETYPE_JSON, + Bytes.toBytes(jsonString)); + Thread.yield(); + return response; + } + +} diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/TestDeleteRow.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/TestDeleteRow.java new file mode 100644 index 0000000..89e1b20 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/TestDeleteRow.java @@ -0,0 +1,99 @@ +/* + * 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 java.io.IOException; + +import javax.xml.bind.JAXBException; + +import org.apache.hadoop.hbase.MediumTests; +import org.apache.hadoop.hbase.rest.client.Response; +import org.junit.Test; +import org.junit.experimental.categories.Category; + + +@Category(MediumTests.class) +public class TestDeleteRow extends RowResourceBase { + + @Test + public void testDeleteNonExistentColumn() throws Exception { + Response response = putValueJson(TABLE, ROW_1, COLUMN_1, VALUE_1); + assertEquals(response.getCode(), 200); + + response = checkAndDeleteJson(TABLE, ROW_1, COLUMN_1, VALUE_2); + assertEquals(304, response.getCode()); + assertEquals(200, getValueJson(TABLE, ROW_1, COLUMN_1).getCode()); + + response = checkAndDeleteJson(TABLE, ROW_2, COLUMN_1, VALUE_2); + assertEquals(304, response.getCode()); + assertEquals(200, getValueJson(TABLE, ROW_1, COLUMN_1).getCode()); + + response = checkAndDeleteJson(TABLE, ROW_1, "dummy", VALUE_1); + assertEquals(400, response.getCode()); + assertEquals(200, getValueJson(TABLE, ROW_1, COLUMN_1).getCode()); + + response = checkAndDeleteJson(TABLE, ROW_1, "dummy:test", VALUE_1); + assertEquals(404, response.getCode()); + assertEquals(200, getValueJson(TABLE, ROW_1, COLUMN_1).getCode()); + + response = checkAndDeleteJson(TABLE, ROW_1, "a:test", VALUE_1); + assertEquals(304, response.getCode()); + assertEquals(200, getValueJson(TABLE, ROW_1, COLUMN_1).getCode()); + } + + @Test + public void testDeleteXML() throws IOException, JAXBException { + Response response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1); + assertEquals(response.getCode(), 200); + response = putValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2); + assertEquals(response.getCode(), 200); + checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1); + checkValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2); + + response = deleteValue(TABLE, ROW_1, COLUMN_1); + assertEquals(response.getCode(), 200); + response = getValueXML(TABLE, ROW_1, COLUMN_1); + assertEquals(response.getCode(), 404); + checkValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2); + + response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1); + assertEquals(response.getCode(), 200); + response = checkAndDeletePB(TABLE, ROW_1, COLUMN_1, VALUE_1); + assertEquals(response.getCode(), 200); + response = getValueXML(TABLE, ROW_1, COLUMN_1); + assertEquals(response.getCode(), 404); + + response = deleteRow(TABLE, ROW_1); + assertEquals(response.getCode(), 200); + response = getValueXML(TABLE, ROW_1, COLUMN_1); + assertEquals(response.getCode(), 404); + response = getValueXML(TABLE, ROW_1, COLUMN_2); + assertEquals(response.getCode(), 404); + + //Delete a row in non existent table + response = deleteValue("dummy", ROW_1, COLUMN_1); + assertEquals(response.getCode(), 404); + + //Delete non existent column + response = deleteValue(TABLE, ROW_1, "dummy"); + assertEquals(response.getCode(), 404); + } + +} diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/TestGetAndPutResource.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/TestGetAndPutResource.java new file mode 100644 index 0000000..6c64f8d --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/TestGetAndPutResource.java @@ -0,0 +1,486 @@ +/** + * 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.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); + + UserGroupInformation ugi = User.getCurrent().getUGI(); + METRICS_ASSERT.assertCounterGt("requests", 2l, + RESTServlet.getInstance(conf, ugi).getMetrics().getSource()); + + METRICS_ASSERT.assertCounterGt("successfulGet", 0l, + RESTServlet.getInstance(conf, ugi).getMetrics().getSource()); + + METRICS_ASSERT.assertCounterGt("successfulPut", 0l, + RESTServlet.getInstance(conf, ugi).getMetrics().getSource()); + + METRICS_ASSERT.assertCounterGt("successfulDelete", 0l, + RESTServlet.getInstance(conf, ugi).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; + } +} + diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/TestMultiRowResource.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/TestMultiRowResource.java index 2aa81bf..0c999b8 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/TestMultiRowResource.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/TestMultiRowResource.java @@ -28,12 +28,15 @@ 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; @@ -151,7 +154,7 @@ public class TestMultiRowResource { } @Test - public void testMultiCellGetJSONNotFound() throws IOException { + public void testMultiCellGetJSONNotFound() throws IOException, JAXBException { String row_5_url = "/" + TABLE + "/" + ROW_1 + "/" + COLUMN_1; StringBuilder path = new StringBuilder(); @@ -163,11 +166,15 @@ public class TestMultiRowResource { 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(), 404); - + 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); } } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/TestRowResource.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/TestRowResource.java deleted file mode 100644 index ceff180..0000000 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/TestRowResource.java +++ /dev/null @@ -1,817 +0,0 @@ -/* - * - * 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.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.CompatibilityFactory; -import org.apache.hadoop.hbase.HBaseTestingUtility; -import org.apache.hadoop.hbase.HColumnDescriptor; -import org.apache.hadoop.hbase.HConstants; -import org.apache.hadoop.hbase.HTableDescriptor; -import org.apache.hadoop.hbase.MediumTests; -import org.apache.hadoop.hbase.TableName; -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.security.User; -import org.apache.hadoop.hbase.test.MetricsAssertHelper; -import org.apache.hadoop.hbase.util.Bytes; -import org.apache.hadoop.security.UserGroupInformation; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.experimental.categories.Category; - -@Category(MediumTests.class) -public class TestRowResource { - 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 COLUMN_3 = CFA + ":"; - private static final String ROW_1 = "testrow1"; - private static final String VALUE_1 = "testvalue1"; - private static final String ROW_2 = "testrow2"; - private static final String VALUE_2 = "testvalue2"; - private static final String ROW_3 = "testrow3"; - private static final String VALUE_3 = "testvalue3"; - private static final String ROW_4 = "testrow4"; - private static final String VALUE_4 = "testvalue4"; - - private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); - private static final HBaseRESTTestingUtility REST_TEST_UTIL = - new HBaseRESTTestingUtility(); - private static final MetricsAssertHelper METRICS_ASSERT = - CompatibilityFactory.getInstance(MetricsAssertHelper.class); - 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(3); - 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())); - } - - @Before - public void beforeMethod() throws Exception { - 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); - } - - @After - public void afterMethod() throws Exception { - HBaseAdmin admin = TEST_UTIL.getHBaseAdmin(); - if (admin.tableExists(TABLE)) { - admin.disableTable(TABLE); - admin.deleteTable(TABLE); - } - } - - @AfterClass - public static void tearDownAfterClass() throws Exception { - REST_TEST_UTIL.shutdownServletContainer(); - TEST_UTIL.shutdownMiniCluster(); - } - - private static Response deleteRow(String table, String row) - throws IOException { - StringBuilder path = new StringBuilder(); - path.append('/'); - path.append(table); - path.append('/'); - path.append(row); - Response response = client.delete(path.toString()); - Thread.yield(); - return response; - } - - private static Response deleteValue(String table, String row, String column) - throws IOException { - StringBuilder path = new StringBuilder(); - path.append('/'); - path.append(table); - path.append('/'); - path.append(row); - path.append('/'); - path.append(column); - Response response = client.delete(path.toString()); - Thread.yield(); - return response; - } - - private static Response getValueXML(String table, String row, String column) - throws IOException { - StringBuilder path = new StringBuilder(); - path.append('/'); - path.append(table); - path.append('/'); - path.append(row); - path.append('/'); - path.append(column); - return getValueXML(path.toString()); - } - - private static Response getValueXML(String table, String startRow, - String endRow, String column) throws IOException { - StringBuilder path = new StringBuilder(); - path.append('/'); - path.append(table); - path.append('/'); - path.append(startRow); - path.append(","); - path.append(endRow); - path.append('/'); - path.append(column); - return getValueXML(path.toString()); - } - - private static Response getValueXML(String url) throws IOException { - Response response = client.get(url, Constants.MIMETYPE_XML); - return response; - } - - private static Response getValuePB(String table, String row, String column) - throws IOException { - StringBuilder path = new StringBuilder(); - path.append('/'); - path.append(table); - path.append('/'); - path.append(row); - path.append('/'); - path.append(column); - return getValuePB(path.toString()); - } - - private static Response getValuePB(String url) throws IOException { - Response response = client.get(url, Constants.MIMETYPE_PROTOBUF); - return response; - } - - private static Response putValueXML(String table, String row, String column, - String value) throws IOException, JAXBException { - StringBuilder path = new StringBuilder(); - path.append('/'); - path.append(table); - path.append('/'); - path.append(row); - path.append('/'); - path.append(column); - return putValueXML(path.toString(), table, row, column, value); - } - - private static Response putValueXML(String url, String table, String row, - String column, String value) throws IOException, JAXBException { - RowModel rowModel = new RowModel(row); - rowModel.addCell(new CellModel(Bytes.toBytes(column), - Bytes.toBytes(value))); - CellSetModel cellSetModel = new CellSetModel(); - cellSetModel.addRow(rowModel); - StringWriter writer = new StringWriter(); - marshaller.marshal(cellSetModel, writer); - Response response = client.put(url, Constants.MIMETYPE_XML, - Bytes.toBytes(writer.toString())); - Thread.yield(); - return response; - } - - private static void checkValueXML(String table, String row, String column, - String value) throws IOException, JAXBException { - Response response = getValueXML(table, row, column); - assertEquals(response.getCode(), 200); - assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type")); - CellSetModel cellSet = (CellSetModel) - unmarshaller.unmarshal(new ByteArrayInputStream(response.getBody())); - RowModel rowModel = cellSet.getRows().get(0); - CellModel cell = rowModel.getCells().get(0); - assertEquals(Bytes.toString(cell.getColumn()), column); - assertEquals(Bytes.toString(cell.getValue()), value); - } - - private static void checkValueXML(String url, String table, String row, - String column, String value) throws IOException, JAXBException { - Response response = getValueXML(url); - assertEquals(response.getCode(), 200); - assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type")); - CellSetModel cellSet = (CellSetModel) - unmarshaller.unmarshal(new ByteArrayInputStream(response.getBody())); - RowModel rowModel = cellSet.getRows().get(0); - CellModel cell = rowModel.getCells().get(0); - assertEquals(Bytes.toString(cell.getColumn()), column); - assertEquals(Bytes.toString(cell.getValue()), value); - } - - private static Response putValuePB(String table, String row, String column, - String value) throws IOException { - StringBuilder path = new StringBuilder(); - path.append('/'); - path.append(table); - path.append('/'); - path.append(row); - path.append('/'); - path.append(column); - return putValuePB(path.toString(), table, row, column, value); - } - - private static Response putValuePB(String url, String table, String row, - String column, String value) throws IOException { - RowModel rowModel = new RowModel(row); - rowModel.addCell(new CellModel(Bytes.toBytes(column), - Bytes.toBytes(value))); - CellSetModel cellSetModel = new CellSetModel(); - cellSetModel.addRow(rowModel); - Response response = client.put(url, Constants.MIMETYPE_PROTOBUF, - cellSetModel.createProtobufOutput()); - Thread.yield(); - return response; - } - - private static void checkValuePB(String table, String row, String column, - String value) throws IOException { - Response response = getValuePB(table, row, column); - assertEquals(response.getCode(), 200); - assertEquals(Constants.MIMETYPE_PROTOBUF, response.getHeader("content-type")); - CellSetModel cellSet = new CellSetModel(); - cellSet.getObjectFromMessage(response.getBody()); - RowModel rowModel = cellSet.getRows().get(0); - CellModel cell = rowModel.getCells().get(0); - assertEquals(Bytes.toString(cell.getColumn()), column); - assertEquals(Bytes.toString(cell.getValue()), value); - } - - private static Response checkAndPutValuePB(String url, String table, - String row, String column, String valueToCheck, String valueToPut) - throws IOException { - RowModel rowModel = new RowModel(row); - rowModel.addCell(new CellModel(Bytes.toBytes(column), - Bytes.toBytes(valueToPut))); - rowModel.addCell(new CellModel(Bytes.toBytes(column), - Bytes.toBytes(valueToCheck))); - CellSetModel cellSetModel = new CellSetModel(); - cellSetModel.addRow(rowModel); - Response response = client.put(url, Constants.MIMETYPE_PROTOBUF, - cellSetModel.createProtobufOutput()); - Thread.yield(); - return response; - } - - private static Response checkAndPutValuePB(String table, String row, - String column, String valueToCheck, String valueToPut) throws IOException { - StringBuilder path = new StringBuilder(); - path.append('/'); - path.append(table); - path.append('/'); - path.append(row); - path.append("?check=put"); - return checkAndPutValuePB(path.toString(), table, row, column, - valueToCheck, valueToPut); - } - - private static Response checkAndPutValueXML(String url, String table, - String row, String column, String valueToCheck, String valueToPut) - throws IOException, JAXBException { - RowModel rowModel = new RowModel(row); - rowModel.addCell(new CellModel(Bytes.toBytes(column), - Bytes.toBytes(valueToPut))); - rowModel.addCell(new CellModel(Bytes.toBytes(column), - Bytes.toBytes(valueToCheck))); - CellSetModel cellSetModel = new CellSetModel(); - cellSetModel.addRow(rowModel); - StringWriter writer = new StringWriter(); - marshaller.marshal(cellSetModel, writer); - Response response = client.put(url, Constants.MIMETYPE_XML, - Bytes.toBytes(writer.toString())); - Thread.yield(); - return response; - } - - private static Response checkAndPutValueXML(String table, String row, - String column, String valueToCheck, String valueToPut) - throws IOException, JAXBException { - StringBuilder path = new StringBuilder(); - path.append('/'); - path.append(table); - path.append('/'); - path.append(row); - path.append("?check=put"); - return checkAndPutValueXML(path.toString(), table, row, column, - valueToCheck, valueToPut); - } - - private static Response checkAndDeleteXML(String url, String table, - String row, String column, String valueToCheck) - throws IOException, JAXBException { - RowModel rowModel = new RowModel(row); - rowModel.addCell(new CellModel(Bytes.toBytes(column), - Bytes.toBytes(valueToCheck))); - CellSetModel cellSetModel = new CellSetModel(); - cellSetModel.addRow(rowModel); - StringWriter writer = new StringWriter(); - marshaller.marshal(cellSetModel, writer); - Response response = client.put(url, Constants.MIMETYPE_XML, - Bytes.toBytes(writer.toString())); - Thread.yield(); - return response; - } - - private static Response checkAndDeleteXML(String table, String row, - String column, String valueToCheck) throws IOException, JAXBException { - StringBuilder path = new StringBuilder(); - path.append('/'); - path.append(table); - path.append('/'); - path.append(row); - path.append("?check=delete"); - return checkAndDeleteXML(path.toString(), table, row, column, valueToCheck); - } - - private static Response checkAndDeletePB(String table, String row, - String column, String value) throws IOException { - StringBuilder path = new StringBuilder(); - path.append('/'); - path.append(table); - path.append('/'); - path.append(row); - path.append("?check=delete"); - return checkAndDeleteValuePB(path.toString(), table, row, column, value); - } - - private static Response checkAndDeleteValuePB(String url, String table, - String row, String column, String valueToCheck) - throws IOException { - RowModel rowModel = new RowModel(row); - rowModel.addCell(new CellModel(Bytes.toBytes(column), Bytes - .toBytes(valueToCheck))); - CellSetModel cellSetModel = new CellSetModel(); - cellSetModel.addRow(rowModel); - Response response = client.put(url, Constants.MIMETYPE_PROTOBUF, - cellSetModel.createProtobufOutput()); - Thread.yield(); - return response; - } - - @Test - public void testDelete() throws IOException, JAXBException { - Response response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1); - assertEquals(response.getCode(), 200); - response = putValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2); - assertEquals(response.getCode(), 200); - checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1); - checkValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2); - - response = deleteValue(TABLE, ROW_1, COLUMN_1); - assertEquals(response.getCode(), 200); - response = getValueXML(TABLE, ROW_1, COLUMN_1); - assertEquals(response.getCode(), 404); - checkValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2); - - response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1); - assertEquals(response.getCode(), 200); - response = checkAndDeletePB(TABLE, ROW_1, COLUMN_1, VALUE_1); - assertEquals(response.getCode(), 200); - response = getValueXML(TABLE, ROW_1, COLUMN_1); - assertEquals(response.getCode(), 404); - - response = deleteRow(TABLE, ROW_1); - assertEquals(response.getCode(), 200); - response = getValueXML(TABLE, ROW_1, COLUMN_1); - assertEquals(response.getCode(), 404); - response = getValueXML(TABLE, ROW_1, COLUMN_2); - assertEquals(response.getCode(), 404); - } - - @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 = 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 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); - - UserGroupInformation ugi = User.getCurrent().getUGI(); - METRICS_ASSERT.assertCounterGt("requests", 2l, - RESTServlet.getInstance(conf, ugi).getMetrics().getSource()); - - METRICS_ASSERT.assertCounterGt("successfulGet", 0l, - RESTServlet.getInstance(conf, ugi).getMetrics().getSource()); - - METRICS_ASSERT.assertCounterGt("successfulPut", 0l, - RESTServlet.getInstance(conf, ugi).getMetrics().getSource()); - - METRICS_ASSERT.assertCounterGt("successfulDelete", 0l, - RESTServlet.getInstance(conf, ugi).getMetrics().getSource()); - } - - @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(); - marshaller.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) - unmarshaller.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(); - marshaller.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 testLatestCellGetXML() throws IOException, JAXBException { - final String path = "/" + TABLE + "/" + ROW_1 + "/" + COLUMN_1; - CellSetModel cellSetModel = new CellSetModel(); - RowModel rowModel = new RowModel(ROW_1); - 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); - StringWriter writer = new StringWriter(); - marshaller.marshal(cellSetModel, writer); - Response response = client.put(path, Constants.MIMETYPE_XML, Bytes.toBytes(writer.toString())); - assertEquals(response.getCode(), 200); - response = getValueXML(TABLE, ROW_1, COLUMN_1); - assertEquals(response.getCode(), 200); - assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type")); - CellSetModel cellSet = (CellSetModel) unmarshaller.unmarshal(new ByteArrayInputStream(response - .getBody())); - 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_1); - assertEquals(response.getCode(), 200); - } - - @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(); - marshaller.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) unmarshaller.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; - } -} - diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteTable.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteTable.java index 56e57dc..76fb800 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteTable.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteTable.java @@ -281,7 +281,7 @@ public class TestRemoteTable { gets.add(new Get(ROW_2)); results = remoteTable.get(gets); assertNotNull(results); - assertEquals(0, results.length); + assertEquals(2, results.length); } @Test