HBASE-26921 Rewrite the counting cells part in TestMultiVersions (#4316)
[hbase.git] / hbase-rest / src / main / java / org / apache / hadoop / hbase / rest / RowResource.java
blobae8cc90eaba0ca8896c5137a17fec74cd8528ded
1 /*
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
20 package org.apache.hadoop.hbase.rest;
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.List;
25 import org.apache.commons.lang3.StringUtils;
26 import org.apache.hadoop.hbase.Cell;
27 import org.apache.hadoop.hbase.Cell.Type;
28 import org.apache.hadoop.hbase.CellBuilderFactory;
29 import org.apache.hadoop.hbase.CellBuilderType;
30 import org.apache.hadoop.hbase.CellUtil;
31 import org.apache.hadoop.hbase.HConstants;
32 import org.apache.hadoop.hbase.client.Append;
33 import org.apache.hadoop.hbase.client.Delete;
34 import org.apache.hadoop.hbase.client.Increment;
35 import org.apache.hadoop.hbase.client.Put;
36 import org.apache.hadoop.hbase.client.Result;
37 import org.apache.hadoop.hbase.client.Table;
38 import org.apache.hadoop.hbase.rest.model.CellModel;
39 import org.apache.hadoop.hbase.rest.model.CellSetModel;
40 import org.apache.hadoop.hbase.rest.model.RowModel;
41 import org.apache.hadoop.hbase.util.Bytes;
42 import org.apache.yetus.audience.InterfaceAudience;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
46 import org.apache.hbase.thirdparty.javax.ws.rs.Consumes;
47 import org.apache.hbase.thirdparty.javax.ws.rs.DELETE;
48 import org.apache.hbase.thirdparty.javax.ws.rs.GET;
49 import org.apache.hbase.thirdparty.javax.ws.rs.POST;
50 import org.apache.hbase.thirdparty.javax.ws.rs.PUT;
51 import org.apache.hbase.thirdparty.javax.ws.rs.Produces;
52 import org.apache.hbase.thirdparty.javax.ws.rs.core.Context;
53 import org.apache.hbase.thirdparty.javax.ws.rs.core.HttpHeaders;
54 import org.apache.hbase.thirdparty.javax.ws.rs.core.MultivaluedMap;
55 import org.apache.hbase.thirdparty.javax.ws.rs.core.Response;
56 import org.apache.hbase.thirdparty.javax.ws.rs.core.Response.ResponseBuilder;
57 import org.apache.hbase.thirdparty.javax.ws.rs.core.UriInfo;
59 @InterfaceAudience.Private
60 public class RowResource extends ResourceBase {
61 private static final Logger LOG = LoggerFactory.getLogger(RowResource.class);
63 private static final String CHECK_PUT = "put";
64 private static final String CHECK_DELETE = "delete";
65 private static final String CHECK_APPEND = "append";
66 private static final String CHECK_INCREMENT = "increment";
68 private TableResource tableResource;
69 private RowSpec rowspec;
70 private String check = null;
71 private boolean returnResult = false;
73 /**
74 * Constructor
75 * @param tableResource
76 * @param rowspec
77 * @param versions
78 * @param check
79 * @param returnResult
80 * @throws IOException
82 public RowResource(TableResource tableResource, String rowspec,
83 String versions, String check, String returnResult) throws IOException {
84 super();
85 this.tableResource = tableResource;
86 this.rowspec = new RowSpec(rowspec);
87 if (versions != null) {
88 this.rowspec.setMaxVersions(Integer.parseInt(versions));
90 this.check = check;
91 if (returnResult != null) {
92 this.returnResult = Boolean.valueOf(returnResult);
96 @GET
97 @Produces({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF,
98 MIMETYPE_PROTOBUF_IETF})
99 public Response get(final @Context UriInfo uriInfo) {
100 if (LOG.isTraceEnabled()) {
101 LOG.trace("GET " + uriInfo.getAbsolutePath());
103 servlet.getMetrics().incrementRequests(1);
104 MultivaluedMap<String, String> params = uriInfo.getQueryParameters();
105 try {
106 ResultGenerator generator =
107 ResultGenerator.fromRowSpec(tableResource.getName(), rowspec, null,
108 !params.containsKey(NOCACHE_PARAM_NAME));
109 if (!generator.hasNext()) {
110 servlet.getMetrics().incrementFailedGetRequests(1);
111 return Response.status(Response.Status.NOT_FOUND)
112 .type(MIMETYPE_TEXT).entity("Not found" + CRLF)
113 .build();
115 int count = 0;
116 CellSetModel model = new CellSetModel();
117 Cell value = generator.next();
118 byte[] rowKey = CellUtil.cloneRow(value);
119 RowModel rowModel = new RowModel(rowKey);
120 do {
121 if (!Bytes.equals(CellUtil.cloneRow(value), rowKey)) {
122 model.addRow(rowModel);
123 rowKey = CellUtil.cloneRow(value);
124 rowModel = new RowModel(rowKey);
126 rowModel.addCell(new CellModel(CellUtil.cloneFamily(value), CellUtil.cloneQualifier(value),
127 value.getTimestamp(), CellUtil.cloneValue(value)));
128 if (++count > rowspec.getMaxValues()) {
129 break;
131 value = generator.next();
132 } while (value != null);
133 model.addRow(rowModel);
134 servlet.getMetrics().incrementSucessfulGetRequests(1);
135 return Response.ok(model).build();
136 } catch (Exception e) {
137 servlet.getMetrics().incrementFailedPutRequests(1);
138 return processException(e);
142 @GET
143 @Produces(MIMETYPE_BINARY)
144 public Response getBinary(final @Context UriInfo uriInfo) {
145 if (LOG.isTraceEnabled()) {
146 LOG.trace("GET " + uriInfo.getAbsolutePath() + " as "+ MIMETYPE_BINARY);
148 servlet.getMetrics().incrementRequests(1);
149 // doesn't make sense to use a non specific coordinate as this can only
150 // return a single cell
151 if (!rowspec.hasColumns() || rowspec.getColumns().length > 1) {
152 servlet.getMetrics().incrementFailedGetRequests(1);
153 return Response.status(Response.Status.BAD_REQUEST).type(MIMETYPE_TEXT)
154 .entity("Bad request: Default 'GET' method only works if there is exactly 1 column " +
155 "in the row. Using the 'Accept' header with one of these formats lets you " +
156 "retrieve the entire row if it has multiple columns: " +
157 // Same as the @Produces list for the get method.
158 MIMETYPE_XML + ", " + MIMETYPE_JSON + ", " +
159 MIMETYPE_PROTOBUF + ", " + MIMETYPE_PROTOBUF_IETF +
160 CRLF).build();
162 MultivaluedMap<String, String> params = uriInfo.getQueryParameters();
163 try {
164 ResultGenerator generator =
165 ResultGenerator.fromRowSpec(tableResource.getName(), rowspec, null,
166 !params.containsKey(NOCACHE_PARAM_NAME));
167 if (!generator.hasNext()) {
168 servlet.getMetrics().incrementFailedGetRequests(1);
169 return Response.status(Response.Status.NOT_FOUND)
170 .type(MIMETYPE_TEXT).entity("Not found" + CRLF)
171 .build();
173 Cell value = generator.next();
174 ResponseBuilder response = Response.ok(CellUtil.cloneValue(value));
175 response.header("X-Timestamp", value.getTimestamp());
176 servlet.getMetrics().incrementSucessfulGetRequests(1);
177 return response.build();
178 } catch (Exception e) {
179 servlet.getMetrics().incrementFailedGetRequests(1);
180 return processException(e);
184 Response update(final CellSetModel model, final boolean replace) {
185 servlet.getMetrics().incrementRequests(1);
186 if (servlet.isReadOnly()) {
187 servlet.getMetrics().incrementFailedPutRequests(1);
188 return Response.status(Response.Status.FORBIDDEN)
189 .type(MIMETYPE_TEXT).entity("Forbidden" + CRLF)
190 .build();
193 if (CHECK_PUT.equalsIgnoreCase(check)) {
194 return checkAndPut(model);
195 } else if (CHECK_DELETE.equalsIgnoreCase(check)) {
196 return checkAndDelete(model);
197 } else if (CHECK_APPEND.equalsIgnoreCase(check)) {
198 return append(model);
199 } else if (CHECK_INCREMENT.equalsIgnoreCase(check)) {
200 return increment(model);
201 } else if (check != null && check.length() > 0) {
202 return Response.status(Response.Status.BAD_REQUEST)
203 .type(MIMETYPE_TEXT).entity("Invalid check value '" + check + "'" + CRLF)
204 .build();
207 Table table = null;
208 try {
209 List<RowModel> rows = model.getRows();
210 List<Put> puts = new ArrayList<>();
211 for (RowModel row: rows) {
212 byte[] key = row.getKey();
213 if (key == null) {
214 key = rowspec.getRow();
216 if (key == null) {
217 servlet.getMetrics().incrementFailedPutRequests(1);
218 return Response.status(Response.Status.BAD_REQUEST)
219 .type(MIMETYPE_TEXT).entity("Bad request: Row key not specified." + CRLF)
220 .build();
222 Put put = new Put(key);
223 int i = 0;
224 for (CellModel cell: row.getCells()) {
225 byte[] col = cell.getColumn();
226 if (col == null) try {
227 col = rowspec.getColumns()[i++];
228 } catch (ArrayIndexOutOfBoundsException e) {
229 col = null;
231 if (col == null) {
232 servlet.getMetrics().incrementFailedPutRequests(1);
233 return Response.status(Response.Status.BAD_REQUEST)
234 .type(MIMETYPE_TEXT).entity("Bad request: Column found to be null." + CRLF)
235 .build();
237 byte [][] parts = CellUtil.parseColumn(col);
238 if (parts.length != 2) {
239 return Response.status(Response.Status.BAD_REQUEST)
240 .type(MIMETYPE_TEXT).entity("Bad request" + CRLF)
241 .build();
243 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
244 .setRow(put.getRow())
245 .setFamily(parts[0])
246 .setQualifier(parts[1])
247 .setTimestamp(cell.getTimestamp())
248 .setType(Type.Put)
249 .setValue(cell.getValue())
250 .build());
252 puts.add(put);
253 if (LOG.isTraceEnabled()) {
254 LOG.trace("PUT " + put.toString());
257 table = servlet.getTable(tableResource.getName());
258 table.put(puts);
259 ResponseBuilder response = Response.ok();
260 servlet.getMetrics().incrementSucessfulPutRequests(1);
261 return response.build();
262 } catch (Exception e) {
263 servlet.getMetrics().incrementFailedPutRequests(1);
264 return processException(e);
265 } finally {
266 if (table != null) try {
267 table.close();
268 } catch (IOException ioe) {
269 LOG.debug("Exception received while closing the table", ioe);
274 // This currently supports only update of one row at a time.
275 Response updateBinary(final byte[] message, final HttpHeaders headers,
276 final boolean replace) {
277 servlet.getMetrics().incrementRequests(1);
278 if (servlet.isReadOnly()) {
279 servlet.getMetrics().incrementFailedPutRequests(1);
280 return Response.status(Response.Status.FORBIDDEN)
281 .type(MIMETYPE_TEXT).entity("Forbidden" + CRLF)
282 .build();
284 Table table = null;
285 try {
286 byte[] row = rowspec.getRow();
287 byte[][] columns = rowspec.getColumns();
288 byte[] column = null;
289 if (columns != null) {
290 column = columns[0];
292 long timestamp = HConstants.LATEST_TIMESTAMP;
293 List<String> vals = headers.getRequestHeader("X-Row");
294 if (vals != null && !vals.isEmpty()) {
295 row = Bytes.toBytes(vals.get(0));
297 vals = headers.getRequestHeader("X-Column");
298 if (vals != null && !vals.isEmpty()) {
299 column = Bytes.toBytes(vals.get(0));
301 vals = headers.getRequestHeader("X-Timestamp");
302 if (vals != null && !vals.isEmpty()) {
303 timestamp = Long.parseLong(vals.get(0));
305 if (column == null) {
306 servlet.getMetrics().incrementFailedPutRequests(1);
307 return Response.status(Response.Status.BAD_REQUEST)
308 .type(MIMETYPE_TEXT).entity("Bad request: Column found to be null." + CRLF)
309 .build();
311 Put put = new Put(row);
312 byte parts[][] = CellUtil.parseColumn(column);
313 if (parts.length != 2) {
314 return Response.status(Response.Status.BAD_REQUEST)
315 .type(MIMETYPE_TEXT).entity("Bad request" + CRLF)
316 .build();
318 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
319 .setRow(put.getRow())
320 .setFamily(parts[0])
321 .setQualifier(parts[1])
322 .setTimestamp(timestamp)
323 .setType(Type.Put)
324 .setValue(message)
325 .build());
326 table = servlet.getTable(tableResource.getName());
327 table.put(put);
328 if (LOG.isTraceEnabled()) {
329 LOG.trace("PUT " + put.toString());
331 servlet.getMetrics().incrementSucessfulPutRequests(1);
332 return Response.ok().build();
333 } catch (Exception e) {
334 servlet.getMetrics().incrementFailedPutRequests(1);
335 return processException(e);
336 } finally {
337 if (table != null) try {
338 table.close();
339 } catch (IOException ioe) {
340 LOG.debug("Exception received while closing the table", ioe);
345 @PUT
346 @Consumes({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF,
347 MIMETYPE_PROTOBUF_IETF})
348 public Response put(final CellSetModel model,
349 final @Context UriInfo uriInfo) {
350 if (LOG.isTraceEnabled()) {
351 LOG.trace("PUT " + uriInfo.getAbsolutePath()
352 + " " + uriInfo.getQueryParameters());
354 return update(model, true);
357 @PUT
358 @Consumes(MIMETYPE_BINARY)
359 public Response putBinary(final byte[] message,
360 final @Context UriInfo uriInfo, final @Context HttpHeaders headers) {
361 if (LOG.isTraceEnabled()) {
362 LOG.trace("PUT " + uriInfo.getAbsolutePath() + " as "+ MIMETYPE_BINARY);
364 return updateBinary(message, headers, true);
367 @POST
368 @Consumes({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF,
369 MIMETYPE_PROTOBUF_IETF})
370 public Response post(final CellSetModel model,
371 final @Context UriInfo uriInfo) {
372 if (LOG.isTraceEnabled()) {
373 LOG.trace("POST " + uriInfo.getAbsolutePath()
374 + " " + uriInfo.getQueryParameters());
376 return update(model, false);
379 @POST
380 @Consumes(MIMETYPE_BINARY)
381 public Response postBinary(final byte[] message,
382 final @Context UriInfo uriInfo, final @Context HttpHeaders headers) {
383 if (LOG.isTraceEnabled()) {
384 LOG.trace("POST " + uriInfo.getAbsolutePath() + " as "+MIMETYPE_BINARY);
386 return updateBinary(message, headers, false);
389 @DELETE
390 public Response delete(final @Context UriInfo uriInfo) {
391 if (LOG.isTraceEnabled()) {
392 LOG.trace("DELETE " + uriInfo.getAbsolutePath());
394 servlet.getMetrics().incrementRequests(1);
395 if (servlet.isReadOnly()) {
396 servlet.getMetrics().incrementFailedDeleteRequests(1);
397 return Response.status(Response.Status.FORBIDDEN)
398 .type(MIMETYPE_TEXT).entity("Forbidden" + CRLF)
399 .build();
401 Delete delete = null;
402 if (rowspec.hasTimestamp())
403 delete = new Delete(rowspec.getRow(), rowspec.getTimestamp());
404 else
405 delete = new Delete(rowspec.getRow());
407 for (byte[] column: rowspec.getColumns()) {
408 byte[][] split = CellUtil.parseColumn(column);
409 if (rowspec.hasTimestamp()) {
410 if (split.length == 1) {
411 delete.addFamily(split[0], rowspec.getTimestamp());
412 } else if (split.length == 2) {
413 delete.addColumns(split[0], split[1], rowspec.getTimestamp());
414 } else {
415 return Response.status(Response.Status.BAD_REQUEST)
416 .type(MIMETYPE_TEXT).entity("Bad request" + CRLF)
417 .build();
419 } else {
420 if (split.length == 1) {
421 delete.addFamily(split[0]);
422 } else if (split.length == 2) {
423 delete.addColumns(split[0], split[1]);
424 } else {
425 return Response.status(Response.Status.BAD_REQUEST)
426 .type(MIMETYPE_TEXT).entity("Bad request" + CRLF)
427 .build();
431 Table table = null;
432 try {
433 table = servlet.getTable(tableResource.getName());
434 table.delete(delete);
435 servlet.getMetrics().incrementSucessfulDeleteRequests(1);
436 if (LOG.isTraceEnabled()) {
437 LOG.trace("DELETE " + delete.toString());
439 } catch (Exception e) {
440 servlet.getMetrics().incrementFailedDeleteRequests(1);
441 return processException(e);
442 } finally {
443 if (table != null) try {
444 table.close();
445 } catch (IOException ioe) {
446 LOG.debug("Exception received while closing the table", ioe);
449 return Response.ok().build();
453 * Validates the input request parameters, parses columns from CellSetModel,
454 * and invokes checkAndPut on HTable.
456 * @param model instance of CellSetModel
457 * @return Response 200 OK, 304 Not modified, 400 Bad request
459 Response checkAndPut(final CellSetModel model) {
460 Table table = null;
461 try {
462 table = servlet.getTable(tableResource.getName());
463 if (model.getRows().size() != 1) {
464 servlet.getMetrics().incrementFailedPutRequests(1);
465 return Response.status(Response.Status.BAD_REQUEST).type(MIMETYPE_TEXT)
466 .entity("Bad request: Number of rows specified is not 1." + CRLF).build();
469 RowModel rowModel = model.getRows().get(0);
470 byte[] key = rowModel.getKey();
471 if (key == null) {
472 key = rowspec.getRow();
475 List<CellModel> cellModels = rowModel.getCells();
476 int cellModelCount = cellModels.size();
477 if (key == null || cellModelCount <= 1) {
478 servlet.getMetrics().incrementFailedPutRequests(1);
479 return Response
480 .status(Response.Status.BAD_REQUEST)
481 .type(MIMETYPE_TEXT)
482 .entity(
483 "Bad request: Either row key is null or no data found for columns specified." + CRLF)
484 .build();
487 Put put = new Put(key);
488 boolean retValue;
489 CellModel valueToCheckCell = cellModels.get(cellModelCount - 1);
490 byte[] valueToCheckColumn = valueToCheckCell.getColumn();
491 byte[][] valueToPutParts = CellUtil.parseColumn(valueToCheckColumn);
492 if (valueToPutParts.length == 2 && valueToPutParts[1].length > 0) {
493 CellModel valueToPutCell = null;
495 // Copy all the cells to the Put request
496 // and track if the check cell's latest value is also sent
497 for (int i = 0, n = cellModelCount - 1; i < n ; i++) {
498 CellModel cell = cellModels.get(i);
499 byte[] col = cell.getColumn();
501 if (col == null) {
502 servlet.getMetrics().incrementFailedPutRequests(1);
503 return Response.status(Response.Status.BAD_REQUEST)
504 .type(MIMETYPE_TEXT).entity("Bad request: Column found to be null." + CRLF)
505 .build();
508 byte [][] parts = CellUtil.parseColumn(col);
510 if (parts.length != 2) {
511 return Response.status(Response.Status.BAD_REQUEST)
512 .type(MIMETYPE_TEXT).entity("Bad request" + CRLF)
513 .build();
515 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
516 .setRow(put.getRow())
517 .setFamily(parts[0])
518 .setQualifier(parts[1])
519 .setTimestamp(cell.getTimestamp())
520 .setType(Type.Put)
521 .setValue(cell.getValue())
522 .build());
523 if(Bytes.equals(col,
524 valueToCheckCell.getColumn())) {
525 valueToPutCell = cell;
529 if (valueToPutCell == null) {
530 servlet.getMetrics().incrementFailedPutRequests(1);
531 return Response.status(Response.Status.BAD_REQUEST).type(MIMETYPE_TEXT)
532 .entity("Bad request: The column to put and check do not match." + CRLF).build();
533 } else {
534 retValue = table.checkAndMutate(key, valueToPutParts[0]).qualifier(valueToPutParts[1])
535 .ifEquals(valueToCheckCell.getValue()).thenPut(put);
537 } else {
538 servlet.getMetrics().incrementFailedPutRequests(1);
539 return Response.status(Response.Status.BAD_REQUEST)
540 .type(MIMETYPE_TEXT).entity("Bad request: Column incorrectly specified." + CRLF)
541 .build();
544 if (LOG.isTraceEnabled()) {
545 LOG.trace("CHECK-AND-PUT " + put.toString() + ", returns " + retValue);
547 if (!retValue) {
548 servlet.getMetrics().incrementFailedPutRequests(1);
549 return Response.status(Response.Status.NOT_MODIFIED)
550 .type(MIMETYPE_TEXT).entity("Value not Modified" + CRLF)
551 .build();
553 ResponseBuilder response = Response.ok();
554 servlet.getMetrics().incrementSucessfulPutRequests(1);
555 return response.build();
556 } catch (Exception e) {
557 servlet.getMetrics().incrementFailedPutRequests(1);
558 return processException(e);
559 } finally {
560 if (table != null) try {
561 table.close();
562 } catch (IOException ioe) {
563 LOG.debug("Exception received while closing the table", ioe);
569 * Validates the input request parameters, parses columns from CellSetModel,
570 * and invokes checkAndDelete on HTable.
572 * @param model instance of CellSetModel
573 * @return Response 200 OK, 304 Not modified, 400 Bad request
575 Response checkAndDelete(final CellSetModel model) {
576 Table table = null;
577 Delete delete = null;
578 try {
579 table = servlet.getTable(tableResource.getName());
580 if (model.getRows().size() != 1) {
581 servlet.getMetrics().incrementFailedDeleteRequests(1);
582 return Response.status(Response.Status.BAD_REQUEST)
583 .type(MIMETYPE_TEXT).entity("Bad request: Number of rows specified is not 1." + CRLF)
584 .build();
586 RowModel rowModel = model.getRows().get(0);
587 byte[] key = rowModel.getKey();
588 if (key == null) {
589 key = rowspec.getRow();
591 if (key == null) {
592 servlet.getMetrics().incrementFailedDeleteRequests(1);
593 return Response.status(Response.Status.BAD_REQUEST)
594 .type(MIMETYPE_TEXT).entity("Bad request: Row key found to be null." + CRLF)
595 .build();
598 List<CellModel> cellModels = rowModel.getCells();
599 int cellModelCount = cellModels.size();
601 delete = new Delete(key);
602 boolean retValue;
603 CellModel valueToDeleteCell = rowModel.getCells().get(cellModelCount -1);
604 byte[] valueToDeleteColumn = valueToDeleteCell.getColumn();
605 if (valueToDeleteColumn == null) {
606 try {
607 valueToDeleteColumn = rowspec.getColumns()[0];
608 } catch (final ArrayIndexOutOfBoundsException e) {
609 servlet.getMetrics().incrementFailedDeleteRequests(1);
610 return Response.status(Response.Status.BAD_REQUEST)
611 .type(MIMETYPE_TEXT).entity("Bad request: Column not specified for check." + CRLF)
612 .build();
616 byte[][] parts ;
617 // Copy all the cells to the Delete request if extra cells are sent
618 if(cellModelCount > 1) {
619 for (int i = 0, n = cellModelCount - 1; i < n; i++) {
620 CellModel cell = cellModels.get(i);
621 byte[] col = cell.getColumn();
623 if (col == null) {
624 servlet.getMetrics().incrementFailedPutRequests(1);
625 return Response.status(Response.Status.BAD_REQUEST)
626 .type(MIMETYPE_TEXT).entity("Bad request: Column found to be null." + CRLF)
627 .build();
630 parts = CellUtil.parseColumn(col);
632 if (parts.length == 1) {
633 // Only Column Family is specified
634 delete.addFamily(parts[0], cell.getTimestamp());
635 } else if (parts.length == 2) {
636 delete.addColumn(parts[0], parts[1], cell.getTimestamp());
637 } else {
638 servlet.getMetrics().incrementFailedDeleteRequests(1);
639 return Response.status(Response.Status.BAD_REQUEST)
640 .type(MIMETYPE_TEXT)
641 .entity("Bad request: Column to delete incorrectly specified." + CRLF)
642 .build();
647 parts = CellUtil.parseColumn(valueToDeleteColumn);
648 if (parts.length == 2) {
649 if (parts[1].length != 0) {
650 // To support backcompat of deleting a cell
651 // if that is the only cell passed to the rest api
652 if(cellModelCount == 1) {
653 delete.addColumns(parts[0], parts[1]);
655 retValue = table.checkAndMutate(key, parts[0]).qualifier(parts[1])
656 .ifEquals(valueToDeleteCell.getValue()).thenDelete(delete);
657 } else {
658 // The case of empty qualifier.
659 if(cellModelCount == 1) {
660 delete.addColumns(parts[0], Bytes.toBytes(StringUtils.EMPTY));
662 retValue = table.checkAndMutate(key, parts[0])
663 .ifEquals(valueToDeleteCell.getValue()).thenDelete(delete);
665 } else {
666 servlet.getMetrics().incrementFailedDeleteRequests(1);
667 return Response.status(Response.Status.BAD_REQUEST)
668 .type(MIMETYPE_TEXT).entity("Bad request: Column to check incorrectly specified." + CRLF)
669 .build();
672 if (LOG.isTraceEnabled()) {
673 LOG.trace("CHECK-AND-DELETE " + delete.toString() + ", returns "
674 + retValue);
677 if (!retValue) {
678 servlet.getMetrics().incrementFailedDeleteRequests(1);
679 return Response.status(Response.Status.NOT_MODIFIED)
680 .type(MIMETYPE_TEXT).entity(" Delete check failed." + CRLF)
681 .build();
683 ResponseBuilder response = Response.ok();
684 servlet.getMetrics().incrementSucessfulDeleteRequests(1);
685 return response.build();
686 } catch (Exception e) {
687 servlet.getMetrics().incrementFailedDeleteRequests(1);
688 return processException(e);
689 } finally {
690 if (table != null) try {
691 table.close();
692 } catch (IOException ioe) {
693 LOG.debug("Exception received while closing the table", ioe);
699 * Validates the input request parameters, parses columns from CellSetModel,
700 * and invokes Append on HTable.
702 * @param model instance of CellSetModel
703 * @return Response 200 OK, 304 Not modified, 400 Bad request
705 Response append(final CellSetModel model) {
706 Table table = null;
707 Append append = null;
708 try {
709 table = servlet.getTable(tableResource.getName());
710 if (model.getRows().size() != 1) {
711 servlet.getMetrics().incrementFailedAppendRequests(1);
712 return Response.status(Response.Status.BAD_REQUEST)
713 .type(MIMETYPE_TEXT).entity("Bad request: Number of rows specified is not 1." + CRLF)
714 .build();
716 RowModel rowModel = model.getRows().get(0);
717 byte[] key = rowModel.getKey();
718 if (key == null) {
719 key = rowspec.getRow();
721 if (key == null) {
722 servlet.getMetrics().incrementFailedAppendRequests(1);
723 return Response.status(Response.Status.BAD_REQUEST)
724 .type(MIMETYPE_TEXT).entity("Bad request: Row key found to be null." + CRLF)
725 .build();
728 append = new Append(key);
729 append.setReturnResults(returnResult);
730 int i = 0;
731 for (CellModel cell: rowModel.getCells()) {
732 byte[] col = cell.getColumn();
733 if (col == null) {
734 try {
735 col = rowspec.getColumns()[i++];
736 } catch (ArrayIndexOutOfBoundsException e) {
737 col = null;
740 if (col == null) {
741 servlet.getMetrics().incrementFailedAppendRequests(1);
742 return Response.status(Response.Status.BAD_REQUEST)
743 .type(MIMETYPE_TEXT).entity("Bad request: Column found to be null." + CRLF)
744 .build();
746 byte [][] parts = CellUtil.parseColumn(col);
747 if (parts.length != 2) {
748 servlet.getMetrics().incrementFailedAppendRequests(1);
749 return Response.status(Response.Status.BAD_REQUEST)
750 .type(MIMETYPE_TEXT).entity("Bad request: Column incorrectly specified." + CRLF)
751 .build();
753 append.addColumn(parts[0], parts[1], cell.getValue());
756 if (LOG.isDebugEnabled()) {
757 LOG.debug("APPEND " + append.toString());
759 Result result = table.append(append);
760 if (returnResult) {
761 if (result.isEmpty()) {
762 servlet.getMetrics().incrementFailedAppendRequests(1);
763 return Response.status(Response.Status.NOT_MODIFIED)
764 .type(MIMETYPE_TEXT).entity("Append return empty." + CRLF)
765 .build();
768 CellSetModel rModel = new CellSetModel();
769 RowModel rRowModel = new RowModel(result.getRow());
770 for (Cell cell : result.listCells()) {
771 rRowModel.addCell(new CellModel(CellUtil.cloneFamily(cell), CellUtil.cloneQualifier(cell),
772 cell.getTimestamp(), CellUtil.cloneValue(cell)));
774 rModel.addRow(rRowModel);
775 servlet.getMetrics().incrementSucessfulAppendRequests(1);
776 return Response.ok(rModel).build();
778 servlet.getMetrics().incrementSucessfulAppendRequests(1);
779 return Response.ok().build();
780 } catch (Exception e) {
781 servlet.getMetrics().incrementFailedAppendRequests(1);
782 return processException(e);
783 } finally {
784 if (table != null) try {
785 table.close();
786 } catch (IOException ioe) {
787 LOG.debug("Exception received while closing the table" + table.getName(), ioe);
793 * Validates the input request parameters, parses columns from CellSetModel,
794 * and invokes Increment on HTable.
796 * @param model instance of CellSetModel
797 * @return Response 200 OK, 304 Not modified, 400 Bad request
799 Response increment(final CellSetModel model) {
800 Table table = null;
801 Increment increment = null;
802 try {
803 table = servlet.getTable(tableResource.getName());
804 if (model.getRows().size() != 1) {
805 servlet.getMetrics().incrementFailedIncrementRequests(1);
806 return Response.status(Response.Status.BAD_REQUEST)
807 .type(MIMETYPE_TEXT).entity("Bad request: Number of rows specified is not 1." + CRLF)
808 .build();
810 RowModel rowModel = model.getRows().get(0);
811 byte[] key = rowModel.getKey();
812 if (key == null) {
813 key = rowspec.getRow();
815 if (key == null) {
816 servlet.getMetrics().incrementFailedIncrementRequests(1);
817 return Response.status(Response.Status.BAD_REQUEST)
818 .type(MIMETYPE_TEXT).entity("Bad request: Row key found to be null." + CRLF)
819 .build();
822 increment = new Increment(key);
823 increment.setReturnResults(returnResult);
824 int i = 0;
825 for (CellModel cell: rowModel.getCells()) {
826 byte[] col = cell.getColumn();
827 if (col == null) {
828 try {
829 col = rowspec.getColumns()[i++];
830 } catch (ArrayIndexOutOfBoundsException e) {
831 col = null;
834 if (col == null) {
835 servlet.getMetrics().incrementFailedIncrementRequests(1);
836 return Response.status(Response.Status.BAD_REQUEST)
837 .type(MIMETYPE_TEXT).entity("Bad request: Column found to be null." + CRLF)
838 .build();
840 byte [][] parts = CellUtil.parseColumn(col);
841 if (parts.length != 2) {
842 servlet.getMetrics().incrementFailedIncrementRequests(1);
843 return Response.status(Response.Status.BAD_REQUEST)
844 .type(MIMETYPE_TEXT).entity("Bad request: Column incorrectly specified." + CRLF)
845 .build();
847 increment.addColumn(parts[0], parts[1], Long.parseLong(Bytes.toStringBinary(cell.getValue())));
850 if (LOG.isDebugEnabled()) {
851 LOG.debug("INCREMENT " + increment.toString());
853 Result result = table.increment(increment);
855 if (returnResult) {
856 if (result.isEmpty()) {
857 servlet.getMetrics().incrementFailedIncrementRequests(1);
858 return Response.status(Response.Status.NOT_MODIFIED)
859 .type(MIMETYPE_TEXT).entity("Increment return empty." + CRLF)
860 .build();
863 CellSetModel rModel = new CellSetModel();
864 RowModel rRowModel = new RowModel(result.getRow());
865 for (Cell cell : result.listCells()) {
866 rRowModel.addCell(new CellModel(CellUtil.cloneFamily(cell), CellUtil.cloneQualifier(cell),
867 cell.getTimestamp(), CellUtil.cloneValue(cell)));
869 rModel.addRow(rowModel);
870 servlet.getMetrics().incrementSucessfulIncrementRequests(1);
871 return Response.ok(rModel).build();
874 ResponseBuilder response = Response.ok();
875 servlet.getMetrics().incrementSucessfulIncrementRequests(1);
876 return response.build();
877 } catch (Exception e) {
878 servlet.getMetrics().incrementFailedIncrementRequests(1);
879 return processException(e);
880 } finally {
881 if (table != null) try {
882 table.close();
883 } catch (IOException ioe) {
884 LOG.debug("Exception received while closing the table " + table.getName(), ioe);