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] 52 lines
[+20]
53

    
   
53

   
54
@InterfaceAudience.Private
54
@InterfaceAudience.Private
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
  static final String CHECK_PUT = "put";

    
   
59
  static final String CHECK_DELETE = "delete";

    
   
60

   
58
  TableResource tableResource;
61
  TableResource tableResource;
59
  RowSpec rowspec;
62
  RowSpec rowspec;

    
   
63
  private String check = null;
60

    
   
64

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

    
   
80
    this.check = check;
76
  }
81
  }
77

    
   
82

   
78
  @GET
83
  @GET
79
  @Produces({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF})
84
  @Produces({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF})
80
  public Response get(final @Context UriInfo uriInfo) {
85
  public Response get(final @Context UriInfo uriInfo) {
[+20] [20] 64 lines
[+20] [+] public Response getBinary(final @Context UriInfo uriInfo) {
145
                  Response.Status.SERVICE_UNAVAILABLE);
150
                  Response.Status.SERVICE_UNAVAILABLE);
146
    }
151
    }
147
  }
152
  }
148

    
   
153

   
149
  Response update(final CellSetModel model, final boolean replace) {
154
  Response update(final CellSetModel model, final boolean replace) {

    
   
155
    if (CHECK_PUT.equalsIgnoreCase(check)) {

    
   
156
      return checkAndPut(model);

    
   
157
    } else if (CHECK_DELETE.equalsIgnoreCase(check)) {

    
   
158
      return checkAndDelete(model);

    
   
159
    } else if (check != null && check.length() > 0) {

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

    
   
161
    }

    
   
162

   
150
    servlet.getMetrics().incrementRequests(1);
163
    servlet.getMetrics().incrementRequests(1);
151
    if (servlet.isReadOnly()) {
164
    if (servlet.isReadOnly()) {
152
      throw new WebApplicationException(Response.Status.FORBIDDEN);
165
      throw new WebApplicationException(Response.Status.FORBIDDEN);
153
    }
166
    }
154
    HTablePool pool = servlet.getTablePool();
167
    HTablePool pool = servlet.getTablePool();
[+20] [20] 211 lines
[+20] [+] public Response delete(final @Context UriInfo uriInfo) {
366
        }
379
        }
367
      }
380
      }
368
    }
381
    }
369
    return Response.ok().build();
382
    return Response.ok().build();
370
  }
383
  }

    
   
384

   

    
   
385
  /**

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

    
   
387
   * and invokes checkAndPut on HTable.

    
   
388
   *

    
   
389
   * @param model instance of CellSetModel

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

    
   
391
   */

    
   
392
  Response checkAndPut(final CellSetModel model) {

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

    
   
394
    if (servlet.isReadOnly()) {

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

    
   
396
    }

    
   
397
    HTablePool pool = servlet.getTablePool();

    
   
398
    HTableInterface table = null;

    
   
399
    try {

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

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

    
   
402
      }

    
   
403

   

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

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

    
   
406
      if (key == null) {

    
   
407
        key = rowspec.getRow();

    
   
408
      }

    
   
409

   

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

    
   
411
      int cellModelCount = cellModels.size();

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

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

    
   
414
      }

    
   
415

   

    
   
416
      Put put = new Put(key);

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

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

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

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

    
   
421
        CellModel valueToPutCell = null;

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

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

    
   
424
              valueToCheckCell.getColumn())) {

    
   
425
            valueToPutCell = cellModels.get(i);

    
   
426
            break;

    
   
427
          }

    
   
428
        }

    
   
429
        if (valueToPutCell != null) {

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

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

    
   
432
        } else {

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

    
   
434
        }

    
   
435
      } else {

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

    
   
437
      }

    
   
438

   

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

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

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

    
   
442
      if (LOG.isDebugEnabled()) {

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

    
   
444
      }

    
   
445
      table.flushCommits();

    
   
446
      ResponseBuilder response = Response.ok();

    
   
447
      if (!retValue) {

    
   
448
        response = Response.status(304);

    
   
449
      }

    
   
450
      return response.build();

    
   
451
    } catch (IOException e) {

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

    
   
453
    } finally {

    
   
454
      try {

    
   
455
        if(table != null){

    
   
456
          pool.putTable(table);

    
   
457
        }

    
   
458
      } catch (Exception ioe) {

    
   
459
        throw new WebApplicationException(ioe,

    
   
460
          Response.Status.SERVICE_UNAVAILABLE);

    
   
461
      }

    
   
462
    }

    
   
463
  }

    
   
464

   

    
   
465
  /**

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

    
   
467
   * and invokes checkAndDelete on HTable.

    
   
468
   *

    
   
469
   * @param model instance of CellSetModel

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

    
   
471
   */

    
   
472
  Response checkAndDelete(final CellSetModel model) {

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

    
   
474
    if (servlet.isReadOnly()) {

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

    
   
476
    }

    
   
477
    HTablePool pool = servlet.getTablePool();

    
   
478
    HTableInterface table = null;

    
   
479
    Delete delete = null;

    
   
480
    try {

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

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

    
   
483
      }

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

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

    
   
486
      if (key == null) {

    
   
487
        key = rowspec.getRow();

    
   
488
      }

    
   
489
      if (key == null) {

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

    
   
491
      }

    
   
492

   

    
   
493
      delete = new Delete(key);

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

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

    
   
496
      if (valueToDeleteColumn == null) {

    
   
497
        try {

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

    
   
499
        } catch (final ArrayIndexOutOfBoundsException e) {

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

    
   
501
        }

    
   
502
      }

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

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

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

    
   
506
      } else {

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

    
   
508
      }

    
   
509

   

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

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

    
   
512
        valueToDeleteCell.getValue(), delete);

    
   
513
      if (LOG.isDebugEnabled()) {

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

    
   
515
          + retValue);

    
   
516
      }

    
   
517
      table.flushCommits();

    
   
518
      ResponseBuilder response = Response.ok();

    
   
519
      if (!retValue) {

    
   
520
        response = Response.status(304);

    
   
521
      }

    
   
522
      return response.build();

    
   
523
    } catch (IOException e) {

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

    
   
525
    } finally {

    
   
526
      try {

    
   
527
        pool.putTable(table);

    
   
528
      } catch (Exception ioe) {

    
   
529
        throw new WebApplicationException(ioe,

    
   
530
          Response.Status.SERVICE_UNAVAILABLE);

    
   
531
      }

    
   
532
    }

    
   
533
  }
371
}
534
}
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...