hbase-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From st...@apache.org
Subject svn commit: r1344400 - in /hbase/branches/0.92: ./ src/main/java/org/apache/hadoop/hbase/rest/ src/main/java/org/apache/hadoop/hbase/rest/client/ src/test/java/org/apache/hadoop/hbase/rest/
Date Wed, 30 May 2012 18:40:57 GMT
Author: stack
Date: Wed May 30 18:40:57 2012
New Revision: 1344400

URL: http://svn.apache.org/viewvc?rev=1344400&view=rev
Log:
HBASE-4720 Implement atomic update operations (checkAndPut, checkAndDelete) for REST client/server

Modified:
    hbase/branches/0.92/CHANGES.txt
    hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/rest/RowResource.java
    hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/rest/TableResource.java
    hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
    hbase/branches/0.92/src/test/java/org/apache/hadoop/hbase/rest/TestRowResource.java

Modified: hbase/branches/0.92/CHANGES.txt
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/CHANGES.txt?rev=1344400&r1=1344399&r2=1344400&view=diff
==============================================================================
--- hbase/branches/0.92/CHANGES.txt (original)
+++ hbase/branches/0.92/CHANGES.txt Wed May 30 18:40:57 2012
@@ -96,6 +96,8 @@ Release 0.92.2 - Unreleased
    HBASE-6097  TestHRegion.testBatchPut is flaky on 0.92 (Gregory Chanan)
    HBASE-6107  Distributed log splitting hangs even there is no task under /hbase/splitlog
    HBASE-6114  CacheControl flags should be tunable per table schema per CF
+   HBASE-4720  Implement atomic update operations (checkAndPut, checkAndDelete) for REST
client/server
+               (Mubarak Seyed and Jimmy Xiang)
 
   NEW FEATURE
    HBASE-5128  [uber hbck] Online automated repair of table integrity and region consistency
problems

Modified: hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/rest/RowResource.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/rest/RowResource.java?rev=1344400&r1=1344399&r2=1344400&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/rest/RowResource.java (original)
+++ hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/rest/RowResource.java Wed May
30 18:40:57 2012
@@ -53,8 +53,12 @@ import org.apache.hadoop.hbase.util.Byte
 public class RowResource extends ResourceBase {
   private static final Log LOG = LogFactory.getLog(RowResource.class);
 
+  static final String CHECK_PUT = "put";
+  static final String CHECK_DELETE = "delete";
+
   TableResource tableResource;
   RowSpec rowspec;
+  private String check = null;
 
   /**
    * Constructor
@@ -64,13 +68,14 @@ public class RowResource extends Resourc
    * @throws IOException
    */
   public RowResource(TableResource tableResource, String rowspec,
-      String versions) throws IOException {
+      String versions, String check) throws IOException {
     super();
     this.tableResource = tableResource;
     this.rowspec = new RowSpec(rowspec);
     if (versions != null) {
       this.rowspec.setMaxVersions(Integer.valueOf(versions));
     }
+    this.check = check;
   }
 
   @GET
@@ -145,6 +150,15 @@ public class RowResource extends Resourc
     if (servlet.isReadOnly()) {
       throw new WebApplicationException(Response.Status.FORBIDDEN);
     }
+
+    if (CHECK_PUT.equalsIgnoreCase(check)) {
+      return checkAndPut(model);
+    } else if (CHECK_DELETE.equalsIgnoreCase(check)) {
+      return checkAndDelete(model);
+    } else if (check != null && check.length() > 0) {
+      LOG.warn("Unknown check value: " + check + ", ignored");
+    }
+
     HTablePool pool = servlet.getTablePool();
     HTableInterface table = null;
     try {
@@ -267,7 +281,8 @@ public class RowResource extends Resourc
   public Response put(final CellSetModel model,
       final @Context UriInfo uriInfo) {
     if (LOG.isDebugEnabled()) {
-      LOG.debug("PUT " + uriInfo.getAbsolutePath());
+      LOG.debug("PUT " + uriInfo.getAbsolutePath()
+        + " " + uriInfo.getQueryParameters());
     }
     return update(model, true);
   }
@@ -287,7 +302,8 @@ public class RowResource extends Resourc
   public Response post(final CellSetModel model,
       final @Context UriInfo uriInfo) {
     if (LOG.isDebugEnabled()) {
-      LOG.debug("POST " + uriInfo.getAbsolutePath());
+      LOG.debug("POST " + uriInfo.getAbsolutePath()
+        + " " + uriInfo.getQueryParameters());
     }
     return update(model, false);
   }
@@ -356,4 +372,146 @@ public class RowResource extends Resourc
     }
     return Response.ok().build();
   }
+
+  /**
+   * Validates the input request parameters, parses columns from CellSetModel,
+   * and invokes checkAndPut on HTable.
+   *
+   * @param model instance of CellSetModel
+   * @return Response 200 OK, 304 Not modified, 400 Bad request
+   */
+  Response checkAndPut(final CellSetModel model) {
+    HTablePool pool = servlet.getTablePool();
+    HTableInterface table = null;
+    try {
+      if (model.getRows().size() != 1) {
+        throw new WebApplicationException(Response.Status.BAD_REQUEST);
+      }
+
+      RowModel rowModel = model.getRows().get(0);
+      byte[] key = rowModel.getKey();
+      if (key == null) {
+        key = rowspec.getRow();
+      }
+
+      List<CellModel> cellModels = rowModel.getCells();
+      int cellModelCount = cellModels.size();
+      if (key == null || cellModelCount <= 1) {
+        throw new WebApplicationException(Response.Status.BAD_REQUEST);
+      }
+
+      Put put = new Put(key);
+      CellModel valueToCheckCell = cellModels.get(cellModelCount - 1);
+      byte[] valueToCheckColumn = valueToCheckCell.getColumn();
+      byte[][] valueToPutParts = KeyValue.parseColumn(valueToCheckColumn);
+      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 (valueToPutCell != null) {
+          put.add(valueToPutParts[0], valueToPutParts[1], valueToPutCell
+            .getTimestamp(), valueToPutCell.getValue());
+        } else {
+          throw new WebApplicationException(Response.Status.BAD_REQUEST);
+        }
+      } else {
+        throw new WebApplicationException(Response.Status.BAD_REQUEST);
+      }
+
+      table = pool.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);
+      }
+      return response.build();
+    } catch (IOException e) {
+      throw new WebApplicationException(e, Response.Status.SERVICE_UNAVAILABLE);
+    } finally {
+      try {
+        if(table != null){
+          pool.putTable(table);
+        }
+      } catch (Exception ioe) {
+        throw new WebApplicationException(ioe,
+          Response.Status.SERVICE_UNAVAILABLE);
+      }
+    }
+  }
+
+  /**
+   * Validates the input request parameters, parses columns from CellSetModel,
+   * and invokes checkAndDelete on HTable.
+   *
+   * @param model instance of CellSetModel
+   * @return Response 200 OK, 304 Not modified, 400 Bad request
+   */
+  Response checkAndDelete(final CellSetModel model) {
+    HTablePool pool = servlet.getTablePool();
+    HTableInterface table = null;
+    Delete delete = null;
+    try {
+      if (model.getRows().size() != 1) {
+        throw new WebApplicationException(Response.Status.BAD_REQUEST);
+      }
+      RowModel rowModel = model.getRows().get(0);
+      byte[] key = rowModel.getKey();
+      if (key == null) {
+        key = rowspec.getRow();
+      }
+      if (key == null) {
+        throw new WebApplicationException(Response.Status.BAD_REQUEST);
+      }
+
+      delete = new Delete(key);
+      CellModel valueToDeleteCell = rowModel.getCells().get(0);
+      byte[] valueToDeleteColumn = valueToDeleteCell.getColumn();
+      if (valueToDeleteColumn == null) {
+        try {
+          valueToDeleteColumn = rowspec.getColumns()[0];
+        } catch (final ArrayIndexOutOfBoundsException e) {
+          throw new WebApplicationException(Response.Status.BAD_REQUEST);
+        }
+      }
+      byte[][] parts = KeyValue.parseColumn(valueToDeleteColumn);
+      if (parts.length == 2 && parts[1].length > 0) {
+        delete.deleteColumns(parts[0], parts[1]);
+      } else {
+        throw new WebApplicationException(Response.Status.BAD_REQUEST);
+      }
+
+      table = pool.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);
+      }
+      return response.build();
+    } catch (IOException e) {
+      throw new WebApplicationException(e, Response.Status.SERVICE_UNAVAILABLE);
+    } finally {
+      try {
+        pool.putTable(table);
+      } catch (Exception ioe) {
+        throw new WebApplicationException(ioe,
+          Response.Status.SERVICE_UNAVAILABLE);
+      }
+    }
+  }
 }

Modified: hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/rest/TableResource.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/rest/TableResource.java?rev=1344400&r1=1344399&r2=1344400&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/rest/TableResource.java (original)
+++ hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/rest/TableResource.java Wed
May 30 18:40:57 2012
@@ -92,7 +92,8 @@ public class TableResource extends Resou
       // We need the @Encoded decorator so Jersey won't urldecode before
       // the RowSpec constructor has a chance to parse
       final @PathParam("rowspec") @Encoded String rowspec,
-      final @QueryParam("v") String versions) throws IOException {
-    return new RowResource(this, rowspec, versions);
+      final @QueryParam("v") String versions,
+      final @QueryParam("check") String check) throws IOException {
+    return new RowResource(this, rowspec, versions, check);
   }
 }

Modified: hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java?rev=1344400&r1=1344399&r2=1344400&view=diff
==============================================================================
--- hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
(original)
+++ hbase/branches/0.92/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
Wed May 30 18:40:57 2012
@@ -584,12 +584,80 @@ public class RemoteHTable implements HTa
 
   public boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier,
       byte[] value, Put put) throws IOException {
-    throw new IOException("checkAndPut not supported");
+    // column to check-the-value
+    put.add(new KeyValue(row, family, qualifier, value));
+
+    CellSetModel model = buildModelFromPut(put);
+    StringBuilder sb = new StringBuilder();
+    sb.append('/');
+    if (accessToken != null) {
+      sb.append(accessToken);
+      sb.append('/');
+    }
+    sb.append(Bytes.toStringBinary(name));
+    sb.append('/');
+    sb.append(Bytes.toStringBinary(put.getRow()));
+    sb.append("?check=put");
+
+    for (int i = 0; i < maxRetries; i++) {
+      Response response = client.put(sb.toString(),
+        Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput());
+      int code = response.getCode();
+      switch (code) {
+      case 200:
+        return true;
+      case 304: // NOT-MODIFIED
+        return false;
+      case 509:
+        try {
+          Thread.sleep(sleepTime);
+        } catch (final InterruptedException e) {
+        }
+        break;
+      default:
+        throw new IOException("checkAndPut request failed with " + code);
+      }
+    }
+    throw new IOException("checkAndPut request timed out");
   }
 
   public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier,
       byte[] value, Delete delete) throws IOException {
-    throw new IOException("checkAndDelete not supported");
+    Put put = new Put(row);
+    // column to check-the-value
+    put.add(new KeyValue(row, family, qualifier, value));
+    CellSetModel model = buildModelFromPut(put);
+    StringBuilder sb = new StringBuilder();
+    sb.append('/');
+    if (accessToken != null) {
+      sb.append(accessToken);
+      sb.append('/');
+    }
+    sb.append(Bytes.toStringBinary(name));
+    sb.append('/');
+    sb.append(Bytes.toStringBinary(row));
+    sb.append("?check=delete");
+
+    for (int i = 0; i < maxRetries; i++) {
+      Response response = client.put(sb.toString(),
+        Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput());
+      int code = response.getCode();
+      switch (code) {
+      case 200:
+        return true;
+      case 304: // NOT-MODIFIED
+        return false;
+      case 509:
+        try {
+          Thread.sleep(sleepTime);
+        } catch (final InterruptedException e) {
+        }
+        break;
+      default:
+        throw new IOException("checkAndDelete request failed with " + code);
+      }
+    }
+    throw new IOException("checkAndDelete request timed out");
   }
 
   public Result increment(Increment increment) throws IOException {

Modified: hbase/branches/0.92/src/test/java/org/apache/hadoop/hbase/rest/TestRowResource.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.92/src/test/java/org/apache/hadoop/hbase/rest/TestRowResource.java?rev=1344400&r1=1344399&r2=1344400&view=diff
==============================================================================
--- hbase/branches/0.92/src/test/java/org/apache/hadoop/hbase/rest/TestRowResource.java (original)
+++ hbase/branches/0.92/src/test/java/org/apache/hadoop/hbase/rest/TestRowResource.java Wed
May 30 18:40:57 2012
@@ -266,11 +266,120 @@ public class TestRowResource {
     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;
-    
-    response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
+    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);
@@ -283,6 +392,13 @@ public class TestRowResource {
     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);
@@ -293,16 +409,20 @@ public class TestRowResource {
 
   @Test
   public void testForbidden() throws IOException, JAXBException {
-    Response response;
-
     conf.set("hbase.rest.readonly", "true");
 
-    response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
+    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);
 
@@ -312,6 +432,10 @@ public class TestRowResource {
     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);
@@ -329,6 +453,11 @@ public class TestRowResource {
     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);
@@ -350,6 +479,13 @@ public class TestRowResource {
     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);
   }



Mime
View raw message