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] 68 lines
[+20] [+] public Response getBinary(final @Context UriInfo uriInfo) {
149
  Response update(final CellSetModel model, final boolean replace) {
154
  Response update(final CellSetModel model, final boolean replace) {
150
    servlet.getMetrics().incrementRequests(1);
155
    servlet.getMetrics().incrementRequests(1);
151
    if (servlet.isReadOnly()) {
156
    if (servlet.isReadOnly()) {
152
      throw new WebApplicationException(Response.Status.FORBIDDEN);
157
      throw new WebApplicationException(Response.Status.FORBIDDEN);
153
    }
158
    }

    
   
159

   

    
   
160
    if (CHECK_PUT.equalsIgnoreCase(check)) {

    
   
161
      return checkAndPut(model);

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

    
   
163
      return checkAndDelete(model);

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

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

    
   
166
    }

    
   
167

   
154
    HTablePool pool = servlet.getTablePool();
168
    HTablePool pool = servlet.getTablePool();
155
    HTableInterface table = null;
169
    HTableInterface table = null;
156
    try {
170
    try {
157
      List<RowModel> rows = model.getRows();
171
      List<RowModel> rows = model.getRows();
158
      List<Put> puts = new ArrayList<Put>();
172
      List<Put> puts = new ArrayList<Put>();
[+20] [20] 116 lines
[+20] public Response getBinary(final @Context UriInfo uriInfo) {
275
  @PUT
289
  @PUT
276
  @Consumes({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF})
290
  @Consumes({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF})
277
  public Response put(final CellSetModel model,
291
  public Response put(final CellSetModel model,
278
      final @Context UriInfo uriInfo) {
292
      final @Context UriInfo uriInfo) {
279
    if (LOG.isDebugEnabled()) {
293
    if (LOG.isDebugEnabled()) {
280
      LOG.debug("PUT " + uriInfo.getAbsolutePath());
294
      LOG.debug("POST " + uriInfo.getAbsolutePath()

    
   
295
        + " " + uriInfo.getQueryParameters());
281
    }
296
    }
282
    return update(model, true);
297
    return update(model, true);
283
  }
298
  }
284

    
   
299

   
285
  @PUT
300
  @PUT
[+20] [20] 9 lines
[+20] [+] public Response putBinary(final byte[] message,
295
  @POST
310
  @POST
296
  @Consumes({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF})
311
  @Consumes({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF})
297
  public Response post(final CellSetModel model,
312
  public Response post(final CellSetModel model,
298
      final @Context UriInfo uriInfo) {
313
      final @Context UriInfo uriInfo) {
299
    if (LOG.isDebugEnabled()) {
314
    if (LOG.isDebugEnabled()) {
300
      LOG.debug("POST " + uriInfo.getAbsolutePath());
315
      LOG.debug("POST " + uriInfo.getAbsolutePath()

    
   
316
        + " " + uriInfo.getQueryParameters());
301
    }
317
    }
302
    return update(model, false);
318
    return update(model, false);
303
  }
319
  }
304

    
   
320

   
305
  @POST
321
  @POST
[+20] [20] 60 lines
[+20] [+] public Response delete(final @Context UriInfo uriInfo) {
366
        }
382
        }
367
      }
383
      }
368
    }
384
    }
369
    return Response.ok().build();
385
    return Response.ok().build();
370
  }
386
  }

    
   
387

   

    
   
388
  /**

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

    
   
390
   * and invokes checkAndPut on HTable.

    
   
391
   *

    
   
392
   * @param model instance of CellSetModel

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

    
   
394
   */

    
   
395
  Response checkAndPut(final CellSetModel model) {

    
   
396
    HTablePool pool = servlet.getTablePool();

    
   
397
    HTableInterface table = null;

    
   
398
    try {

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

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

    
   
401
      }

    
   
402

   

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

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

    
   
405
      if (key == null) {

    
   
406
        key = rowspec.getRow();

    
   
407
      }

    
   
408

   

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

    
   
410
      int cellModelCount = cellModels.size();

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

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

    
   
413
      }

    
   
414

   

    
   
415
      Put put = new Put(key);

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

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

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

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

    
   
420
        CellModel valueToPutCell = null;

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

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

    
   
423
              valueToCheckCell.getColumn())) {

    
   
424
            valueToPutCell = cellModels.get(i);

    
   
425
            break;

    
   
426
          }

    
   
427
        }

    
   
428
        if (valueToPutCell != null) {

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

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

    
   
431
        } else {

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

    
   
433
        }

    
   
434
      } else {

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

    
   
436
      }

    
   
437

   

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

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

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

    
   
441
      if (LOG.isDebugEnabled()) {

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

    
   
443
      }

    
   
444
      table.flushCommits();

    
   
445
      ResponseBuilder response = Response.ok();

    
   
446
      if (!retValue) {

    
   
447
        response = Response.status(304);

    
   
448
      }

    
   
449
      return response.build();

    
   
450
    } catch (IOException e) {

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

    
   
452
    } finally {

    
   
453
      try {

    
   
454
        if(table != null){

    
   
455
          pool.putTable(table);

    
   
456
        }

    
   
457
      } catch (Exception ioe) {

    
   
458
        throw new WebApplicationException(ioe,

    
   
459
          Response.Status.SERVICE_UNAVAILABLE);

    
   
460
      }

    
   
461
    }

    
   
462
  }

    
   
463

   

    
   
464
  /**

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

    
   
466
   * and invokes checkAndDelete on HTable.

    
   
467
   *

    
   
468
   * @param model instance of CellSetModel

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

    
   
470
   */

    
   
471
  Response checkAndDelete(final CellSetModel model) {

    
   
472
    HTablePool pool = servlet.getTablePool();

    
   
473
    HTableInterface table = null;

    
   
474
    Delete delete = null;

    
   
475
    try {

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

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

    
   
478
      }

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

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

    
   
481
      if (key == null) {

    
   
482
        key = rowspec.getRow();

    
   
483
      }

    
   
484
      if (key == null) {

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

    
   
486
      }

    
   
487

   

    
   
488
      delete = new Delete(key);

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

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

    
   
491
      if (valueToDeleteColumn == null) {

    
   
492
        try {

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

    
   
494
        } catch (final ArrayIndexOutOfBoundsException e) {

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

    
   
496
        }

    
   
497
      }

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

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

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

    
   
501
      } else {

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

    
   
503
      }

    
   
504

   

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

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

    
   
507
        valueToDeleteCell.getValue(), delete);

    
   
508
      if (LOG.isDebugEnabled()) {

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

    
   
510
          + retValue);

    
   
511
      }

    
   
512
      table.flushCommits();

    
   
513
      ResponseBuilder response = Response.ok();

    
   
514
      if (!retValue) {

    
   
515
        response = Response.status(304);

    
   
516
      }

    
   
517
      return response.build();

    
   
518
    } catch (IOException e) {

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

    
   
520
    } finally {

    
   
521
      try {

    
   
522
        pool.putTable(table);

    
   
523
      } catch (Exception ioe) {

    
   
524
        throw new WebApplicationException(ioe,

    
   
525
          Response.Status.SERVICE_UNAVAILABLE);

    
   
526
      }

    
   
527
    }

    
   
528
  }
371
}
529
}
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...