Revert "HBASE-26523 Upgrade hbase-thirdparty dependency to 4.0.0 (#3910)"
[hbase.git] / hbase-rest / src / main / java / org / apache / hadoop / hbase / rest / RowResource.java
blob3ac747238dd9f787c25fbd98b1740ea24aadf9af
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;
26 import javax.ws.rs.Consumes;
27 import javax.ws.rs.DELETE;
28 import javax.ws.rs.GET;
29 import javax.ws.rs.POST;
30 import javax.ws.rs.PUT;
31 import javax.ws.rs.Produces;
32 import javax.ws.rs.core.Context;
33 import javax.ws.rs.core.HttpHeaders;
34 import javax.ws.rs.core.MultivaluedMap;
35 import javax.ws.rs.core.Response;
36 import javax.ws.rs.core.Response.ResponseBuilder;
37 import javax.ws.rs.core.UriInfo;
39 import org.apache.commons.lang3.StringUtils;
40 import org.apache.hadoop.hbase.Cell;
41 import org.apache.hadoop.hbase.Cell.Type;
42 import org.apache.hadoop.hbase.CellBuilderFactory;
43 import org.apache.hadoop.hbase.CellBuilderType;
44 import org.apache.hadoop.hbase.CellUtil;
45 import org.apache.hadoop.hbase.HConstants;
46 import org.apache.hadoop.hbase.client.Append;
47 import org.apache.hadoop.hbase.client.Delete;
48 import org.apache.hadoop.hbase.client.Increment;
49 import org.apache.hadoop.hbase.client.Put;
50 import org.apache.hadoop.hbase.client.Result;
51 import org.apache.hadoop.hbase.client.Table;
52 import org.apache.hadoop.hbase.rest.model.CellModel;
53 import org.apache.hadoop.hbase.rest.model.CellSetModel;
54 import org.apache.hadoop.hbase.rest.model.RowModel;
55 import org.apache.hadoop.hbase.util.Bytes;
56 import org.apache.yetus.audience.InterfaceAudience;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
60 @InterfaceAudience.Private
61 public class RowResource extends ResourceBase {
62 private static final Logger LOG = LoggerFactory.getLogger(RowResource.class);
64 private static final String CHECK_PUT = "put";
65 private static final String CHECK_DELETE = "delete";
66 private static final String CHECK_APPEND = "append";
67 private static final String CHECK_INCREMENT = "increment";
69 private TableResource tableResource;
70 private RowSpec rowspec;
71 private String check = null;
72 private boolean returnResult = false;
74 /**
75 * Constructor
76 * @param tableResource
77 * @param rowspec
78 * @param versions
79 * @param check
80 * @param returnResult
81 * @throws IOException
83 public RowResource(TableResource tableResource, String rowspec,
84 String versions, String check, String returnResult) throws IOException {
85 super();
86 this.tableResource = tableResource;
87 this.rowspec = new RowSpec(rowspec);
88 if (versions != null) {
89 this.rowspec.setMaxVersions(Integer.parseInt(versions));
91 this.check = check;
92 if (returnResult != null) {
93 this.returnResult = Boolean.valueOf(returnResult);
97 @GET
98 @Produces({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF,
99 MIMETYPE_PROTOBUF_IETF})
100 public Response get(final @Context UriInfo uriInfo) {
101 if (LOG.isTraceEnabled()) {
102 LOG.trace("GET " + uriInfo.getAbsolutePath());
104 servlet.getMetrics().incrementRequests(1);
105 MultivaluedMap<String, String> params = uriInfo.getQueryParameters();
106 try {
107 ResultGenerator generator =
108 ResultGenerator.fromRowSpec(tableResource.getName(), rowspec, null,
109 !params.containsKey(NOCACHE_PARAM_NAME));
110 if (!generator.hasNext()) {
111 servlet.getMetrics().incrementFailedGetRequests(1);
112 return Response.status(Response.Status.NOT_FOUND)
113 .type(MIMETYPE_TEXT).entity("Not found" + CRLF)
114 .build();
116 int count = 0;
117 CellSetModel model = new CellSetModel();
118 Cell value = generator.next();
119 byte[] rowKey = CellUtil.cloneRow(value);
120 RowModel rowModel = new RowModel(rowKey);
121 do {
122 if (!Bytes.equals(CellUtil.cloneRow(value), rowKey)) {
123 model.addRow(rowModel);
124 rowKey = CellUtil.cloneRow(value);
125 rowModel = new RowModel(rowKey);
127 rowModel.addCell(new CellModel(CellUtil.cloneFamily(value), CellUtil.cloneQualifier(value),
128 value.getTimestamp(), CellUtil.cloneValue(value)));
129 if (++count > rowspec.getMaxValues()) {
130 break;
132 value = generator.next();
133 } while (value != null);
134 model.addRow(rowModel);
135 servlet.getMetrics().incrementSucessfulGetRequests(1);
136 return Response.ok(model).build();
137 } catch (Exception e) {
138 servlet.getMetrics().incrementFailedPutRequests(1);
139 return processException(e);
143 @GET
144 @Produces(MIMETYPE_BINARY)
145 public Response getBinary(final @Context UriInfo uriInfo) {
146 if (LOG.isTraceEnabled()) {
147 LOG.trace("GET " + uriInfo.getAbsolutePath() + " as "+ MIMETYPE_BINARY);
149 servlet.getMetrics().incrementRequests(1);
150 // doesn't make sense to use a non specific coordinate as this can only
151 // return a single cell
152 if (!rowspec.hasColumns() || rowspec.getColumns().length > 1) {
153 servlet.getMetrics().incrementFailedGetRequests(1);
154 return Response.status(Response.Status.BAD_REQUEST).type(MIMETYPE_TEXT)
155 .entity("Bad request: Default 'GET' method only works if there is exactly 1 column " +
156 "in the row. Using the 'Accept' header with one of these formats lets you " +
157 "retrieve the entire row if it has multiple columns: " +
158 // Same as the @Produces list for the get method.
159 MIMETYPE_XML + ", " + MIMETYPE_JSON + ", " +
160 MIMETYPE_PROTOBUF + ", " + MIMETYPE_PROTOBUF_IETF +
161 CRLF).build();
163 MultivaluedMap<String, String> params = uriInfo.getQueryParameters();
164 try {
165 ResultGenerator generator =
166 ResultGenerator.fromRowSpec(tableResource.getName(), rowspec, null,
167 !params.containsKey(NOCACHE_PARAM_NAME));
168 if (!generator.hasNext()) {
169 servlet.getMetrics().incrementFailedGetRequests(1);
170 return Response.status(Response.Status.NOT_FOUND)
171 .type(MIMETYPE_TEXT).entity("Not found" + CRLF)
172 .build();
174 Cell value = generator.next();
175 ResponseBuilder response = Response.ok(CellUtil.cloneValue(value));
176 response.header("X-Timestamp", value.getTimestamp());
177 servlet.getMetrics().incrementSucessfulGetRequests(1);
178 return response.build();
179 } catch (Exception e) {
180 servlet.getMetrics().incrementFailedGetRequests(1);
181 return processException(e);
185 Response update(final CellSetModel model, final boolean replace) {
186 servlet.getMetrics().incrementRequests(1);
187 if (servlet.isReadOnly()) {
188 servlet.getMetrics().incrementFailedPutRequests(1);
189 return Response.status(Response.Status.FORBIDDEN)
190 .type(MIMETYPE_TEXT).entity("Forbidden" + CRLF)
191 .build();
194 if (CHECK_PUT.equalsIgnoreCase(check)) {
195 return checkAndPut(model);
196 } else if (CHECK_DELETE.equalsIgnoreCase(check)) {
197 return checkAndDelete(model);
198 } else if (CHECK_APPEND.equalsIgnoreCase(check)) {
199 return append(model);
200 } else if (CHECK_INCREMENT.equalsIgnoreCase(check)) {
201 return increment(model);
202 } else if (check != null && check.length() > 0) {
203 return Response.status(Response.Status.BAD_REQUEST)
204 .type(MIMETYPE_TEXT).entity("Invalid check value '" + check + "'" + CRLF)
205 .build();
208 Table table = null;
209 try {
210 List<RowModel> rows = model.getRows();
211 List<Put> puts = new ArrayList<>();
212 for (RowModel row: rows) {
213 byte[] key = row.getKey();
214 if (key == null) {
215 key = rowspec.getRow();
217 if (key == null) {
218 servlet.getMetrics().incrementFailedPutRequests(1);
219 return Response.status(Response.Status.BAD_REQUEST)
220 .type(MIMETYPE_TEXT).entity("Bad request: Row key not specified." + CRLF)
221 .build();
223 Put put = new Put(key);
224 int i = 0;
225 for (CellModel cell: row.getCells()) {
226 byte[] col = cell.getColumn();
227 if (col == null) try {
228 col = rowspec.getColumns()[i++];
229 } catch (ArrayIndexOutOfBoundsException e) {
230 col = null;
232 if (col == null) {
233 servlet.getMetrics().incrementFailedPutRequests(1);
234 return Response.status(Response.Status.BAD_REQUEST)
235 .type(MIMETYPE_TEXT).entity("Bad request: Column found to be null." + CRLF)
236 .build();
238 byte [][] parts = CellUtil.parseColumn(col);
239 if (parts.length != 2) {
240 return Response.status(Response.Status.BAD_REQUEST)
241 .type(MIMETYPE_TEXT).entity("Bad request" + CRLF)
242 .build();
244 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
245 .setRow(put.getRow())
246 .setFamily(parts[0])
247 .setQualifier(parts[1])
248 .setTimestamp(cell.getTimestamp())
249 .setType(Type.Put)
250 .setValue(cell.getValue())
251 .build());
253 puts.add(put);
254 if (LOG.isTraceEnabled()) {
255 LOG.trace("PUT " + put.toString());
258 table = servlet.getTable(tableResource.getName());
259 table.put(puts);
260 ResponseBuilder response = Response.ok();
261 servlet.getMetrics().incrementSucessfulPutRequests(1);
262 return response.build();
263 } catch (Exception e) {
264 servlet.getMetrics().incrementFailedPutRequests(1);
265 return processException(e);
266 } finally {
267 if (table != null) try {
268 table.close();
269 } catch (IOException ioe) {
270 LOG.debug("Exception received while closing the table", ioe);
275 // This currently supports only update of one row at a time.
276 Response updateBinary(final byte[] message, final HttpHeaders headers,
277 final boolean replace) {
278 servlet.getMetrics().incrementRequests(1);
279 if (servlet.isReadOnly()) {
280 servlet.getMetrics().incrementFailedPutRequests(1);
281 return Response.status(Response.Status.FORBIDDEN)
282 .type(MIMETYPE_TEXT).entity("Forbidden" + CRLF)
283 .build();
285 Table table = null;
286 try {
287 byte[] row = rowspec.getRow();
288 byte[][] columns = rowspec.getColumns();
289 byte[] column = null;
290 if (columns != null) {
291 column = columns[0];
293 long timestamp = HConstants.LATEST_TIMESTAMP;
294 List<String> vals = headers.getRequestHeader("X-Row");
295 if (vals != null && !vals.isEmpty()) {
296 row = Bytes.toBytes(vals.get(0));
298 vals = headers.getRequestHeader("X-Column");
299 if (vals != null && !vals.isEmpty()) {
300 column = Bytes.toBytes(vals.get(0));
302 vals = headers.getRequestHeader("X-Timestamp");
303 if (vals != null && !vals.isEmpty()) {
304 timestamp = Long.parseLong(vals.get(0));
306 if (column == null) {
307 servlet.getMetrics().incrementFailedPutRequests(1);
308 return Response.status(Response.Status.BAD_REQUEST)
309 .type(MIMETYPE_TEXT).entity("Bad request: Column found to be null." + CRLF)
310 .build();
312 Put put = new Put(row);
313 byte parts[][] = CellUtil.parseColumn(column);
314 if (parts.length != 2) {
315 return Response.status(Response.Status.BAD_REQUEST)
316 .type(MIMETYPE_TEXT).entity("Bad request" + CRLF)
317 .build();
319 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
320 .setRow(put.getRow())
321 .setFamily(parts[0])
322 .setQualifier(parts[1])
323 .setTimestamp(timestamp)
324 .setType(Type.Put)
325 .setValue(message)
326 .build());
327 table = servlet.getTable(tableResource.getName());
328 table.put(put);
329 if (LOG.isTraceEnabled()) {
330 LOG.trace("PUT " + put.toString());
332 servlet.getMetrics().incrementSucessfulPutRequests(1);
333 return Response.ok().build();
334 } catch (Exception e) {
335 servlet.getMetrics().incrementFailedPutRequests(1);
336 return processException(e);
337 } finally {
338 if (table != null) try {
339 table.close();
340 } catch (IOException ioe) {
341 LOG.debug("Exception received while closing the table", ioe);
346 @PUT
347 @Consumes({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF,
348 MIMETYPE_PROTOBUF_IETF})
349 public Response put(final CellSetModel model,
350 final @Context UriInfo uriInfo) {
351 if (LOG.isTraceEnabled()) {
352 LOG.trace("PUT " + uriInfo.getAbsolutePath()
353 + " " + uriInfo.getQueryParameters());
355 return update(model, true);
358 @PUT
359 @Consumes(MIMETYPE_BINARY)
360 public Response putBinary(final byte[] message,
361 final @Context UriInfo uriInfo, final @Context HttpHeaders headers) {
362 if (LOG.isTraceEnabled()) {
363 LOG.trace("PUT " + uriInfo.getAbsolutePath() + " as "+ MIMETYPE_BINARY);
365 return updateBinary(message, headers, true);
368 @POST
369 @Consumes({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF,
370 MIMETYPE_PROTOBUF_IETF})
371 public Response post(final CellSetModel model,
372 final @Context UriInfo uriInfo) {
373 if (LOG.isTraceEnabled()) {
374 LOG.trace("POST " + uriInfo.getAbsolutePath()
375 + " " + uriInfo.getQueryParameters());
377 return update(model, false);
380 @POST
381 @Consumes(MIMETYPE_BINARY)
382 public Response postBinary(final byte[] message,
383 final @Context UriInfo uriInfo, final @Context HttpHeaders headers) {
384 if (LOG.isTraceEnabled()) {
385 LOG.trace("POST " + uriInfo.getAbsolutePath() + " as "+MIMETYPE_BINARY);
387 return updateBinary(message, headers, false);
390 @DELETE
391 public Response delete(final @Context UriInfo uriInfo) {
392 if (LOG.isTraceEnabled()) {
393 LOG.trace("DELETE " + uriInfo.getAbsolutePath());
395 servlet.getMetrics().incrementRequests(1);
396 if (servlet.isReadOnly()) {
397 servlet.getMetrics().incrementFailedDeleteRequests(1);
398 return Response.status(Response.Status.FORBIDDEN)
399 .type(MIMETYPE_TEXT).entity("Forbidden" + CRLF)
400 .build();
402 Delete delete = null;
403 if (rowspec.hasTimestamp())
404 delete = new Delete(rowspec.getRow(), rowspec.getTimestamp());
405 else
406 delete = new Delete(rowspec.getRow());
408 for (byte[] column: rowspec.getColumns()) {
409 byte[][] split = CellUtil.parseColumn(column);
410 if (rowspec.hasTimestamp()) {
411 if (split.length == 1) {
412 delete.addFamily(split[0], rowspec.getTimestamp());
413 } else if (split.length == 2) {
414 delete.addColumns(split[0], split[1], rowspec.getTimestamp());
415 } else {
416 return Response.status(Response.Status.BAD_REQUEST)
417 .type(MIMETYPE_TEXT).entity("Bad request" + CRLF)
418 .build();
420 } else {
421 if (split.length == 1) {
422 delete.addFamily(split[0]);
423 } else if (split.length == 2) {
424 delete.addColumns(split[0], split[1]);
425 } else {
426 return Response.status(Response.Status.BAD_REQUEST)
427 .type(MIMETYPE_TEXT).entity("Bad request" + CRLF)
428 .build();
432 Table table = null;
433 try {
434 table = servlet.getTable(tableResource.getName());
435 table.delete(delete);
436 servlet.getMetrics().incrementSucessfulDeleteRequests(1);
437 if (LOG.isTraceEnabled()) {
438 LOG.trace("DELETE " + delete.toString());
440 } catch (Exception e) {
441 servlet.getMetrics().incrementFailedDeleteRequests(1);
442 return processException(e);
443 } finally {
444 if (table != null) try {
445 table.close();
446 } catch (IOException ioe) {
447 LOG.debug("Exception received while closing the table", ioe);
450 return Response.ok().build();
454 * Validates the input request parameters, parses columns from CellSetModel,
455 * and invokes checkAndPut on HTable.
457 * @param model instance of CellSetModel
458 * @return Response 200 OK, 304 Not modified, 400 Bad request
460 Response checkAndPut(final CellSetModel model) {
461 Table table = null;
462 try {
463 table = servlet.getTable(tableResource.getName());
464 if (model.getRows().size() != 1) {
465 servlet.getMetrics().incrementFailedPutRequests(1);
466 return Response.status(Response.Status.BAD_REQUEST).type(MIMETYPE_TEXT)
467 .entity("Bad request: Number of rows specified is not 1." + CRLF).build();
470 RowModel rowModel = model.getRows().get(0);
471 byte[] key = rowModel.getKey();
472 if (key == null) {
473 key = rowspec.getRow();
476 List<CellModel> cellModels = rowModel.getCells();
477 int cellModelCount = cellModels.size();
478 if (key == null || cellModelCount <= 1) {
479 servlet.getMetrics().incrementFailedPutRequests(1);
480 return Response
481 .status(Response.Status.BAD_REQUEST)
482 .type(MIMETYPE_TEXT)
483 .entity(
484 "Bad request: Either row key is null or no data found for columns specified." + CRLF)
485 .build();
488 Put put = new Put(key);
489 boolean retValue;
490 CellModel valueToCheckCell = cellModels.get(cellModelCount - 1);
491 byte[] valueToCheckColumn = valueToCheckCell.getColumn();
492 byte[][] valueToPutParts = CellUtil.parseColumn(valueToCheckColumn);
493 if (valueToPutParts.length == 2 && valueToPutParts[1].length > 0) {
494 CellModel valueToPutCell = null;
496 // Copy all the cells to the Put request
497 // and track if the check cell's latest value is also sent
498 for (int i = 0, n = cellModelCount - 1; i < n ; i++) {
499 CellModel cell = cellModels.get(i);
500 byte[] col = cell.getColumn();
502 if (col == null) {
503 servlet.getMetrics().incrementFailedPutRequests(1);
504 return Response.status(Response.Status.BAD_REQUEST)
505 .type(MIMETYPE_TEXT).entity("Bad request: Column found to be null." + CRLF)
506 .build();
509 byte [][] parts = CellUtil.parseColumn(col);
511 if (parts.length != 2) {
512 return Response.status(Response.Status.BAD_REQUEST)
513 .type(MIMETYPE_TEXT).entity("Bad request" + CRLF)
514 .build();
516 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
517 .setRow(put.getRow())
518 .setFamily(parts[0])
519 .setQualifier(parts[1])
520 .setTimestamp(cell.getTimestamp())
521 .setType(Type.Put)
522 .setValue(cell.getValue())
523 .build());
524 if(Bytes.equals(col,
525 valueToCheckCell.getColumn())) {
526 valueToPutCell = cell;
530 if (valueToPutCell == null) {
531 servlet.getMetrics().incrementFailedPutRequests(1);
532 return Response.status(Response.Status.BAD_REQUEST).type(MIMETYPE_TEXT)
533 .entity("Bad request: The column to put and check do not match." + CRLF).build();
534 } else {
535 retValue = table.checkAndMutate(key, valueToPutParts[0]).qualifier(valueToPutParts[1])
536 .ifEquals(valueToCheckCell.getValue()).thenPut(put);
538 } else {
539 servlet.getMetrics().incrementFailedPutRequests(1);
540 return Response.status(Response.Status.BAD_REQUEST)
541 .type(MIMETYPE_TEXT).entity("Bad request: Column incorrectly specified." + CRLF)
542 .build();
545 if (LOG.isTraceEnabled()) {
546 LOG.trace("CHECK-AND-PUT " + put.toString() + ", returns " + retValue);
548 if (!retValue) {
549 servlet.getMetrics().incrementFailedPutRequests(1);
550 return Response.status(Response.Status.NOT_MODIFIED)
551 .type(MIMETYPE_TEXT).entity("Value not Modified" + CRLF)
552 .build();
554 ResponseBuilder response = Response.ok();
555 servlet.getMetrics().incrementSucessfulPutRequests(1);
556 return response.build();
557 } catch (Exception e) {
558 servlet.getMetrics().incrementFailedPutRequests(1);
559 return processException(e);
560 } finally {
561 if (table != null) try {
562 table.close();
563 } catch (IOException ioe) {
564 LOG.debug("Exception received while closing the table", ioe);
570 * Validates the input request parameters, parses columns from CellSetModel,
571 * and invokes checkAndDelete on HTable.
573 * @param model instance of CellSetModel
574 * @return Response 200 OK, 304 Not modified, 400 Bad request
576 Response checkAndDelete(final CellSetModel model) {
577 Table table = null;
578 Delete delete = null;
579 try {
580 table = servlet.getTable(tableResource.getName());
581 if (model.getRows().size() != 1) {
582 servlet.getMetrics().incrementFailedDeleteRequests(1);
583 return Response.status(Response.Status.BAD_REQUEST)
584 .type(MIMETYPE_TEXT).entity("Bad request: Number of rows specified is not 1." + CRLF)
585 .build();
587 RowModel rowModel = model.getRows().get(0);
588 byte[] key = rowModel.getKey();
589 if (key == null) {
590 key = rowspec.getRow();
592 if (key == null) {
593 servlet.getMetrics().incrementFailedDeleteRequests(1);
594 return Response.status(Response.Status.BAD_REQUEST)
595 .type(MIMETYPE_TEXT).entity("Bad request: Row key found to be null." + CRLF)
596 .build();
599 List<CellModel> cellModels = rowModel.getCells();
600 int cellModelCount = cellModels.size();
602 delete = new Delete(key);
603 boolean retValue;
604 CellModel valueToDeleteCell = rowModel.getCells().get(cellModelCount -1);
605 byte[] valueToDeleteColumn = valueToDeleteCell.getColumn();
606 if (valueToDeleteColumn == null) {
607 try {
608 valueToDeleteColumn = rowspec.getColumns()[0];
609 } catch (final ArrayIndexOutOfBoundsException e) {
610 servlet.getMetrics().incrementFailedDeleteRequests(1);
611 return Response.status(Response.Status.BAD_REQUEST)
612 .type(MIMETYPE_TEXT).entity("Bad request: Column not specified for check." + CRLF)
613 .build();
617 byte[][] parts ;
618 // Copy all the cells to the Delete request if extra cells are sent
619 if(cellModelCount > 1) {
620 for (int i = 0, n = cellModelCount - 1; i < n; i++) {
621 CellModel cell = cellModels.get(i);
622 byte[] col = cell.getColumn();
624 if (col == null) {
625 servlet.getMetrics().incrementFailedPutRequests(1);
626 return Response.status(Response.Status.BAD_REQUEST)
627 .type(MIMETYPE_TEXT).entity("Bad request: Column found to be null." + CRLF)
628 .build();
631 parts = CellUtil.parseColumn(col);
633 if (parts.length == 1) {
634 // Only Column Family is specified
635 delete.addFamily(parts[0], cell.getTimestamp());
636 } else if (parts.length == 2) {
637 delete.addColumn(parts[0], parts[1], cell.getTimestamp());
638 } else {
639 servlet.getMetrics().incrementFailedDeleteRequests(1);
640 return Response.status(Response.Status.BAD_REQUEST)
641 .type(MIMETYPE_TEXT)
642 .entity("Bad request: Column to delete incorrectly specified." + CRLF)
643 .build();
648 parts = CellUtil.parseColumn(valueToDeleteColumn);
649 if (parts.length == 2) {
650 if (parts[1].length != 0) {
651 // To support backcompat of deleting a cell
652 // if that is the only cell passed to the rest api
653 if(cellModelCount == 1) {
654 delete.addColumns(parts[0], parts[1]);
656 retValue = table.checkAndMutate(key, parts[0]).qualifier(parts[1])
657 .ifEquals(valueToDeleteCell.getValue()).thenDelete(delete);
658 } else {
659 // The case of empty qualifier.
660 if(cellModelCount == 1) {
661 delete.addColumns(parts[0], Bytes.toBytes(StringUtils.EMPTY));
663 retValue = table.checkAndMutate(key, parts[0])
664 .ifEquals(valueToDeleteCell.getValue()).thenDelete(delete);
666 } else {
667 servlet.getMetrics().incrementFailedDeleteRequests(1);
668 return Response.status(Response.Status.BAD_REQUEST)
669 .type(MIMETYPE_TEXT).entity("Bad request: Column to check incorrectly specified." + CRLF)
670 .build();
673 if (LOG.isTraceEnabled()) {
674 LOG.trace("CHECK-AND-DELETE " + delete.toString() + ", returns "
675 + retValue);
678 if (!retValue) {
679 servlet.getMetrics().incrementFailedDeleteRequests(1);
680 return Response.status(Response.Status.NOT_MODIFIED)
681 .type(MIMETYPE_TEXT).entity(" Delete check failed." + CRLF)
682 .build();
684 ResponseBuilder response = Response.ok();
685 servlet.getMetrics().incrementSucessfulDeleteRequests(1);
686 return response.build();
687 } catch (Exception e) {
688 servlet.getMetrics().incrementFailedDeleteRequests(1);
689 return processException(e);
690 } finally {
691 if (table != null) try {
692 table.close();
693 } catch (IOException ioe) {
694 LOG.debug("Exception received while closing the table", ioe);
700 * Validates the input request parameters, parses columns from CellSetModel,
701 * and invokes Append on HTable.
703 * @param model instance of CellSetModel
704 * @return Response 200 OK, 304 Not modified, 400 Bad request
706 Response append(final CellSetModel model) {
707 Table table = null;
708 Append append = null;
709 try {
710 table = servlet.getTable(tableResource.getName());
711 if (model.getRows().size() != 1) {
712 servlet.getMetrics().incrementFailedAppendRequests(1);
713 return Response.status(Response.Status.BAD_REQUEST)
714 .type(MIMETYPE_TEXT).entity("Bad request: Number of rows specified is not 1." + CRLF)
715 .build();
717 RowModel rowModel = model.getRows().get(0);
718 byte[] key = rowModel.getKey();
719 if (key == null) {
720 key = rowspec.getRow();
722 if (key == null) {
723 servlet.getMetrics().incrementFailedAppendRequests(1);
724 return Response.status(Response.Status.BAD_REQUEST)
725 .type(MIMETYPE_TEXT).entity("Bad request: Row key found to be null." + CRLF)
726 .build();
729 append = new Append(key);
730 append.setReturnResults(returnResult);
731 int i = 0;
732 for (CellModel cell: rowModel.getCells()) {
733 byte[] col = cell.getColumn();
734 if (col == null) {
735 try {
736 col = rowspec.getColumns()[i++];
737 } catch (ArrayIndexOutOfBoundsException e) {
738 col = null;
741 if (col == null) {
742 servlet.getMetrics().incrementFailedAppendRequests(1);
743 return Response.status(Response.Status.BAD_REQUEST)
744 .type(MIMETYPE_TEXT).entity("Bad request: Column found to be null." + CRLF)
745 .build();
747 byte [][] parts = CellUtil.parseColumn(col);
748 if (parts.length != 2) {
749 servlet.getMetrics().incrementFailedAppendRequests(1);
750 return Response.status(Response.Status.BAD_REQUEST)
751 .type(MIMETYPE_TEXT).entity("Bad request: Column incorrectly specified." + CRLF)
752 .build();
754 append.addColumn(parts[0], parts[1], cell.getValue());
757 if (LOG.isDebugEnabled()) {
758 LOG.debug("APPEND " + append.toString());
760 Result result = table.append(append);
761 if (returnResult) {
762 if (result.isEmpty()) {
763 servlet.getMetrics().incrementFailedAppendRequests(1);
764 return Response.status(Response.Status.NOT_MODIFIED)
765 .type(MIMETYPE_TEXT).entity("Append return empty." + CRLF)
766 .build();
769 CellSetModel rModel = new CellSetModel();
770 RowModel rRowModel = new RowModel(result.getRow());
771 for (Cell cell : result.listCells()) {
772 rRowModel.addCell(new CellModel(CellUtil.cloneFamily(cell), CellUtil.cloneQualifier(cell),
773 cell.getTimestamp(), CellUtil.cloneValue(cell)));
775 rModel.addRow(rRowModel);
776 servlet.getMetrics().incrementSucessfulAppendRequests(1);
777 return Response.ok(rModel).build();
779 servlet.getMetrics().incrementSucessfulAppendRequests(1);
780 return Response.ok().build();
781 } catch (Exception e) {
782 servlet.getMetrics().incrementFailedAppendRequests(1);
783 return processException(e);
784 } finally {
785 if (table != null) try {
786 table.close();
787 } catch (IOException ioe) {
788 LOG.debug("Exception received while closing the table" + table.getName(), ioe);
794 * Validates the input request parameters, parses columns from CellSetModel,
795 * and invokes Increment on HTable.
797 * @param model instance of CellSetModel
798 * @return Response 200 OK, 304 Not modified, 400 Bad request
800 Response increment(final CellSetModel model) {
801 Table table = null;
802 Increment increment = null;
803 try {
804 table = servlet.getTable(tableResource.getName());
805 if (model.getRows().size() != 1) {
806 servlet.getMetrics().incrementFailedIncrementRequests(1);
807 return Response.status(Response.Status.BAD_REQUEST)
808 .type(MIMETYPE_TEXT).entity("Bad request: Number of rows specified is not 1." + CRLF)
809 .build();
811 RowModel rowModel = model.getRows().get(0);
812 byte[] key = rowModel.getKey();
813 if (key == null) {
814 key = rowspec.getRow();
816 if (key == null) {
817 servlet.getMetrics().incrementFailedIncrementRequests(1);
818 return Response.status(Response.Status.BAD_REQUEST)
819 .type(MIMETYPE_TEXT).entity("Bad request: Row key found to be null." + CRLF)
820 .build();
823 increment = new Increment(key);
824 increment.setReturnResults(returnResult);
825 int i = 0;
826 for (CellModel cell: rowModel.getCells()) {
827 byte[] col = cell.getColumn();
828 if (col == null) {
829 try {
830 col = rowspec.getColumns()[i++];
831 } catch (ArrayIndexOutOfBoundsException e) {
832 col = null;
835 if (col == null) {
836 servlet.getMetrics().incrementFailedIncrementRequests(1);
837 return Response.status(Response.Status.BAD_REQUEST)
838 .type(MIMETYPE_TEXT).entity("Bad request: Column found to be null." + CRLF)
839 .build();
841 byte [][] parts = CellUtil.parseColumn(col);
842 if (parts.length != 2) {
843 servlet.getMetrics().incrementFailedIncrementRequests(1);
844 return Response.status(Response.Status.BAD_REQUEST)
845 .type(MIMETYPE_TEXT).entity("Bad request: Column incorrectly specified." + CRLF)
846 .build();
848 increment.addColumn(parts[0], parts[1], Long.parseLong(Bytes.toStringBinary(cell.getValue())));
851 if (LOG.isDebugEnabled()) {
852 LOG.debug("INCREMENT " + increment.toString());
854 Result result = table.increment(increment);
856 if (returnResult) {
857 if (result.isEmpty()) {
858 servlet.getMetrics().incrementFailedIncrementRequests(1);
859 return Response.status(Response.Status.NOT_MODIFIED)
860 .type(MIMETYPE_TEXT).entity("Increment return empty." + CRLF)
861 .build();
864 CellSetModel rModel = new CellSetModel();
865 RowModel rRowModel = new RowModel(result.getRow());
866 for (Cell cell : result.listCells()) {
867 rRowModel.addCell(new CellModel(CellUtil.cloneFamily(cell), CellUtil.cloneQualifier(cell),
868 cell.getTimestamp(), CellUtil.cloneValue(cell)));
870 rModel.addRow(rowModel);
871 servlet.getMetrics().incrementSucessfulIncrementRequests(1);
872 return Response.ok(rModel).build();
875 ResponseBuilder response = Response.ok();
876 servlet.getMetrics().incrementSucessfulIncrementRequests(1);
877 return response.build();
878 } catch (Exception e) {
879 servlet.getMetrics().incrementFailedIncrementRequests(1);
880 return processException(e);
881 } finally {
882 if (table != null) try {
883 table.close();
884 } catch (IOException ioe) {
885 LOG.debug("Exception received while closing the table " + table.getName(), ioe);