Review Board 1.7.22


HBASE-4720 Implement atomic update operations (checkAndPut, checkAndDelete) for REST client/server

Review Request #5259 - Created May 29, 2012 and submitted

Jimmy Xiang
trunk
HBASE-4720
Reviewers
hbase
apurtell, jdcryans, stack, tedyu
hbase-git
Basically, it is Mubarak's patch.  I enhanced a little bit.  The jira is dormant for a while.  I'd like to get it going.
Will backport to 94, 92 and 90 after it's integrated into trunk.
mvn -PrunMediumTests -Dtest=Test*Resource clean test
hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RowResource.java
Revision c5a54b2 New Change
[20] 54 lines
[+20]
55
public class RowResource extends ResourceBase {
55
public class RowResource extends ResourceBase {
56
  private static final Log LOG = LogFactory.getLog(RowResource.class);
56
  private static final Log LOG = LogFactory.getLog(RowResource.class);
57

    
   
57

   
58
  TableResource tableResource;
58
  TableResource tableResource;
59
  RowSpec rowspec;
59
  RowSpec rowspec;

    
   
60
  private String check = null;
60

    
   
61

   
61
  /**
62
  /**
62
   * Constructor
63
   * Constructor
63
   * @param tableResource
64
   * @param tableResource
64
   * @param rowspec
65
   * @param rowspec
65
   * @param versions
66
   * @param versions
66
   * @throws IOException
67
   * @throws IOException
67
   */
68
   */
68
  public RowResource(TableResource tableResource, String rowspec,
69
  public RowResource(TableResource tableResource, String rowspec,
69
      String versions) throws IOException {
70
      String versions, String check) throws IOException {
70
    super();
71
    super();
71
    this.tableResource = tableResource;
72
    this.tableResource = tableResource;
72
    this.rowspec = new RowSpec(rowspec);
73
    this.rowspec = new RowSpec(rowspec);
73
    if (versions != null) {
74
    if (versions != null) {
74
      this.rowspec.setMaxVersions(Integer.valueOf(versions));
75
      this.rowspec.setMaxVersions(Integer.valueOf(versions));
75
    }
76
    }

    
   
77
    this.check = check;
76
  }
78
  }
77

    
   
79

   
78
  @GET
80
  @GET
79
  @Produces({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF})
81
  @Produces({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF})
80
  public Response get(final @Context UriInfo uriInfo) {
82
  public Response get(final @Context UriInfo uriInfo) {
[+20] [20] 196 lines
[+20] [+] public Response getBinary(final @Context UriInfo uriInfo) {
277
  public Response put(final CellSetModel model,
279
  public Response put(final CellSetModel model,
278
      final @Context UriInfo uriInfo) {
280
      final @Context UriInfo uriInfo) {
279
    if (LOG.isDebugEnabled()) {
281
    if (LOG.isDebugEnabled()) {
280
      LOG.debug("PUT " + uriInfo.getAbsolutePath());
282
      LOG.debug("PUT " + uriInfo.getAbsolutePath());
281
    }
283
    }

    
   
284
    if ("put".equals(check)) {

    
   
285
      return checkAndPut(model);

    
   
286
    } else if ("delete".equals(check)) {

    
   
287
      return checkAndDelete(model);

    
   
288
    } else {

    
   
289
      if (check != null && check.length() > 0) {

    
   
290
        LOG.warn("Unknown check value: " + check + ", ignored");

    
   
291
      }
282
    return update(model, true);
292
      return update(model, true);
283
  }
293
    }

    
   
294
  }
284

    
   
295

   
285
  @PUT
296
  @PUT
286
  @Consumes(MIMETYPE_BINARY)
297
  @Consumes(MIMETYPE_BINARY)
287
  public Response putBinary(final byte[] message,
298
  public Response putBinary(final byte[] message,
288
      final @Context UriInfo uriInfo, final @Context HttpHeaders headers) {
299
      final @Context UriInfo uriInfo, final @Context HttpHeaders headers) {
[+20] [20] 8 lines
[+20] public Response putBinary(final byte[] message,
297
  public Response post(final CellSetModel model,
308
  public Response post(final CellSetModel model,
298
      final @Context UriInfo uriInfo) {
309
      final @Context UriInfo uriInfo) {
299
    if (LOG.isDebugEnabled()) {
310
    if (LOG.isDebugEnabled()) {
300
      LOG.debug("POST " + uriInfo.getAbsolutePath());
311
      LOG.debug("POST " + uriInfo.getAbsolutePath());
301
    }
312
    }

    
   
313
    if ("put".equals(check)) {

    
   
314
      return checkAndPut(model);

    
   
315
    } else if ("delete".equals(check)) {

    
   
316
      return checkAndDelete(model);

    
   
317
    } else {

    
   
318
      if (check != null && check.length() > 0) {

    
   
319
        LOG.warn("Unknown check value: " + check + ", ignored");

    
   
320
      }
302
    return update(model, false);
321
      return update(model, false);
303
  }
322
    }

    
   
323
  }
304

    
   
324

   
305
  @POST
325
  @POST
306
  @Consumes(MIMETYPE_BINARY)
326
  @Consumes(MIMETYPE_BINARY)
307
  public Response postBinary(final byte[] message,
327
  public Response postBinary(final byte[] message,
308
      final @Context UriInfo uriInfo, final @Context HttpHeaders headers) {
328
      final @Context UriInfo uriInfo, final @Context HttpHeaders headers) {
[+20] [20] 57 lines
[+20] [+] public Response delete(final @Context UriInfo uriInfo) {
366
        }
386
        }
367
      }
387
      }
368
    }
388
    }
369
    return Response.ok().build();
389
    return Response.ok().build();
370
  }
390
  }

    
   
391

   

    
   
392
  /**

    
   
393
   * Validates the input request parameters, parses columns from CellSetModel,

    
   
394
   * and invokes checkAndPut on HTable.

    
   
395
   *

    
   
396
   * @param model instance of CellSetModel

    
   
397
   * @return Response 200 OK, 304 Not modified, 400 Bad request

    
   
398
   */

    
   
399
  Response checkAndPut(final CellSetModel model) {

    
   
400
    servlet.getMetrics().incrementRequests(1);

    
   
401
    if (servlet.isReadOnly()) {

    
   
402
      throw new WebApplicationException(Response.Status.FORBIDDEN);

    
   
403
    }

    
   
404
    HTablePool pool = servlet.getTablePool();

    
   
405
    HTableInterface table = null;

    
   
406
    try {

    
   
407
      if (model.getRows().size() != 1) {

    
   
408
        throw new WebApplicationException(Response.Status.BAD_REQUEST);

    
   
409
      }

    
   
410

   

    
   
411
      RowModel rowModel = model.getRows().get(0);

    
   
412
      byte[] key = rowModel.getKey();

    
   
413
      if (key == null) {

    
   
414
        key = rowspec.getRow();

    
   
415
      }

    
   
416

   

    
   
417
      List<CellModel> cellModels = rowModel.getCells();

    
   
418
      int cellModelCount = cellModels.size();

    
   
419
      if (key == null || cellModelCount <= 1) {

    
   
420
        throw new WebApplicationException(Response.Status.BAD_REQUEST);

    
   
421
      }

    
   
422

   

    
   
423
      Put put = new Put(key);

    
   
424
      CellModel valueToCheckCell = cellModels.get(cellModelCount - 1);

    
   
425
      byte[] valueToCheckColumn = valueToCheckCell.getColumn();

    
   
426
      byte[][] valueToPutParts = KeyValue.parseColumn(valueToCheckColumn);

    
   
427
      if (valueToPutParts.length == 2 && valueToPutParts[1].length > 0) {

    
   
428
        CellModel valueToPutCell = null;

    
   
429
        for (int i = 0, n = cellModelCount - 1; i < n ; i++) {

    
   
430
          if(Bytes.equals(cellModels.get(i).getColumn(),

    
   
431
              valueToCheckCell.getColumn())) {

    
   
432
            valueToPutCell = cellModels.get(i);

    
   
433
            break;

    
   
434
          }

    
   
435
        }

    
   
436
        if (valueToPutCell != null) {

    
   
437
          put.add(valueToPutParts[0], valueToPutParts[1], valueToPutCell

    
   
438
            .getTimestamp(), valueToPutCell.getValue());

    
   
439
        } else {

    
   
440
          throw new WebApplicationException(Response.Status.BAD_REQUEST);

    
   
441
        }

    
   
442
      } else {

    
   
443
        throw new WebApplicationException(Response.Status.BAD_REQUEST);

    
   
444
      }

    
   
445

   

    
   
446
      table = pool.getTable(this.tableResource.getName());

    
   
447
      boolean retValue = table.checkAndPut(key, valueToPutParts[0],

    
   
448
        valueToPutParts[1], valueToCheckCell.getValue(), put);

    
   
449
      if (LOG.isDebugEnabled()) {

    
   
450
        LOG.debug("CHECK-AND-PUT " + put.toString() + ", returns " + retValue);

    
   
451
      }

    
   
452
      table.flushCommits();

    
   
453
      ResponseBuilder response = Response.ok();

    
   
454
      if (!retValue) {

    
   
455
        response = Response.status(304);

    
   
456
      }

    
   
457
      return response.build();

    
   
458
    } catch (IOException e) {

    
   
459
      throw new WebApplicationException(e, Response.Status.SERVICE_UNAVAILABLE);

    
   
460
    } finally {

    
   
461
      try {

    
   
462
        if(table != null){

    
   
463
          pool.putTable(table);

    
   
464
        }

    
   
465
      } catch (Exception ioe) {

    
   
466
        throw new WebApplicationException(ioe,

    
   
467
          Response.Status.SERVICE_UNAVAILABLE);

    
   
468
      }

    
   
469
    }

    
   
470
  }

    
   
471

   

    
   
472
  /**

    
   
473
   * Validates the input request parameters, parses columns from CellSetModel,

    
   
474
   * and invokes checkAndDelete on HTable.

    
   
475
   *

    
   
476
   * @param model instance of CellSetModel

    
   
477
   * @return Response 200 OK, 304 Not modified, 400 Bad request

    
   
478
   */

    
   
479
  Response checkAndDelete(final CellSetModel model) {

    
   
480
    servlet.getMetrics().incrementRequests(1);

    
   
481
    if (servlet.isReadOnly()) {

    
   
482
      throw new WebApplicationException(Response.Status.FORBIDDEN);

    
   
483
    }

    
   
484
    HTablePool pool = servlet.getTablePool();

    
   
485
    HTableInterface table = null;

    
   
486
    Delete delete = null;

    
   
487
    try {

    
   
488
      if (model.getRows().size() != 1) {

    
   
489
        throw new WebApplicationException(Response.Status.BAD_REQUEST);

    
   
490
      }

    
   
491
      RowModel rowModel = model.getRows().get(0);

    
   
492
      byte[] key = rowModel.getKey();

    
   
493
      if (key == null) {

    
   
494
        key = rowspec.getRow();

    
   
495
      }

    
   
496
      if (key == null) {

    
   
497
        throw new WebApplicationException(Response.Status.BAD_REQUEST);

    
   
498
      }

    
   
499

   

    
   
500
      delete = new Delete(key);

    
   
501
      CellModel valueToDeleteCell = rowModel.getCells().get(0);

    
   
502
      byte[] valueToDeleteColumn = valueToDeleteCell.getColumn();

    
   
503
      if (valueToDeleteColumn == null) {

    
   
504
        try {

    
   
505
          valueToDeleteColumn = rowspec.getColumns()[0];

    
   
506
        } catch (final ArrayIndexOutOfBoundsException e) {

    
   
507
          throw new WebApplicationException(Response.Status.BAD_REQUEST);

    
   
508
        }

    
   
509
      }

    
   
510
      byte[][] parts = KeyValue.parseColumn(valueToDeleteColumn);

    
   
511
      if (parts.length == 2 && parts[1].length > 0) {

    
   
512
        delete.deleteColumns(parts[0], parts[1]);

    
   
513
      } else {

    
   
514
        throw new WebApplicationException(Response.Status.BAD_REQUEST);

    
   
515
      }

    
   
516

   

    
   
517
      table = pool.getTable(tableResource.getName());

    
   
518
      boolean retValue = table.checkAndDelete(key, parts[0], parts[1],

    
   
519
        valueToDeleteCell.getValue(), delete);

    
   
520
      if (LOG.isDebugEnabled()) {

    
   
521
        LOG.debug("CHECK-AND-DELETE " + delete.toString() + ", returns "

    
   
522
          + retValue);

    
   
523
      }

    
   
524
      table.flushCommits();

    
   
525
      ResponseBuilder response = Response.ok();

    
   
526
      if (!retValue) {

    
   
527
        response = Response.status(304);

    
   
528
      }

    
   
529
      return response.build();

    
   
530
    } catch (IOException e) {

    
   
531
      throw new WebApplicationException(e, Response.Status.SERVICE_UNAVAILABLE);

    
   
532
    } finally {

    
   
533
      try {

    
   
534
        pool.putTable(table);

    
   
535
      } catch (Exception ioe) {

    
   
536
        throw new WebApplicationException(ioe,

    
   
537
          Response.Status.SERVICE_UNAVAILABLE);

    
   
538
      }

    
   
539
    }

    
   
540
  }
371
}
541
}
hbase-server/src/main/java/org/apache/hadoop/hbase/rest/TableResource.java
Revision d314604 New Change
 
hbase-server/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
Revision ccfe878 New Change
 
hbase-server/src/test/java/org/apache/hadoop/hbase/rest/TestRowResource.java
Revision b59436c New Change
 
  1. hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RowResource.java: Loading...
  2. hbase-server/src/main/java/org/apache/hadoop/hbase/rest/TableResource.java: Loading...
  3. hbase-server/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java: Loading...
  4. hbase-server/src/test/java/org/apache/hadoop/hbase/rest/TestRowResource.java: Loading...