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;
75 * @param tableResource
82 public RowResource(TableResource tableResource
, String rowspec
,
83 String versions
, String check
, String returnResult
) throws IOException
{
85 this.tableResource
= tableResource
;
86 this.rowspec
= new RowSpec(rowspec
);
87 if (versions
!= null) {
88 this.rowspec
.setMaxVersions(Integer
.parseInt(versions
));
91 if (returnResult
!= null) {
92 this.returnResult
= Boolean
.valueOf(returnResult
);
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();
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
)
116 CellSetModel model
= new CellSetModel();
117 Cell value
= generator
.next();
118 byte[] rowKey
= CellUtil
.cloneRow(value
);
119 RowModel rowModel
= new RowModel(rowKey
);
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()) {
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
);
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
+
162 MultivaluedMap
<String
, String
> params
= uriInfo
.getQueryParameters();
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
)
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
)
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
)
209 List
<RowModel
> rows
= model
.getRows();
210 List
<Put
> puts
= new ArrayList
<>();
211 for (RowModel row
: rows
) {
212 byte[] key
= row
.getKey();
214 key
= rowspec
.getRow();
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
)
222 Put put
= new Put(key
);
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
) {
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
)
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
)
243 put
.add(CellBuilderFactory
.create(CellBuilderType
.SHALLOW_COPY
)
244 .setRow(put
.getRow())
246 .setQualifier(parts
[1])
247 .setTimestamp(cell
.getTimestamp())
249 .setValue(cell
.getValue())
253 if (LOG
.isTraceEnabled()) {
254 LOG
.trace("PUT " + put
.toString());
257 table
= servlet
.getTable(tableResource
.getName());
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
);
266 if (table
!= null) try {
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
)
286 byte[] row
= rowspec
.getRow();
287 byte[][] columns
= rowspec
.getColumns();
288 byte[] column
= null;
289 if (columns
!= null) {
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
)
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
)
318 put
.add(CellBuilderFactory
.create(CellBuilderType
.SHALLOW_COPY
)
319 .setRow(put
.getRow())
321 .setQualifier(parts
[1])
322 .setTimestamp(timestamp
)
326 table
= servlet
.getTable(tableResource
.getName());
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
);
337 if (table
!= null) try {
339 } catch (IOException ioe
) {
340 LOG
.debug("Exception received while closing the table", ioe
);
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);
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);
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);
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);
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
)
401 Delete delete
= null;
402 if (rowspec
.hasTimestamp())
403 delete
= new Delete(rowspec
.getRow(), rowspec
.getTimestamp());
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());
415 return Response
.status(Response
.Status
.BAD_REQUEST
)
416 .type(MIMETYPE_TEXT
).entity("Bad request" + CRLF
)
420 if (split
.length
== 1) {
421 delete
.addFamily(split
[0]);
422 } else if (split
.length
== 2) {
423 delete
.addColumns(split
[0], split
[1]);
425 return Response
.status(Response
.Status
.BAD_REQUEST
)
426 .type(MIMETYPE_TEXT
).entity("Bad request" + CRLF
)
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
);
443 if (table
!= null) try {
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
) {
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();
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);
480 .status(Response
.Status
.BAD_REQUEST
)
483 "Bad request: Either row key is null or no data found for columns specified." + CRLF
)
487 Put put
= new Put(key
);
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();
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
)
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
)
515 put
.add(CellBuilderFactory
.create(CellBuilderType
.SHALLOW_COPY
)
516 .setRow(put
.getRow())
518 .setQualifier(parts
[1])
519 .setTimestamp(cell
.getTimestamp())
521 .setValue(cell
.getValue())
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();
534 retValue
= table
.checkAndMutate(key
, valueToPutParts
[0]).qualifier(valueToPutParts
[1])
535 .ifEquals(valueToCheckCell
.getValue()).thenPut(put
);
538 servlet
.getMetrics().incrementFailedPutRequests(1);
539 return Response
.status(Response
.Status
.BAD_REQUEST
)
540 .type(MIMETYPE_TEXT
).entity("Bad request: Column incorrectly specified." + CRLF
)
544 if (LOG
.isTraceEnabled()) {
545 LOG
.trace("CHECK-AND-PUT " + put
.toString() + ", returns " + retValue
);
548 servlet
.getMetrics().incrementFailedPutRequests(1);
549 return Response
.status(Response
.Status
.NOT_MODIFIED
)
550 .type(MIMETYPE_TEXT
).entity("Value not Modified" + CRLF
)
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
);
560 if (table
!= null) try {
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
) {
577 Delete delete
= null;
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
)
586 RowModel rowModel
= model
.getRows().get(0);
587 byte[] key
= rowModel
.getKey();
589 key
= rowspec
.getRow();
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
)
598 List
<CellModel
> cellModels
= rowModel
.getCells();
599 int cellModelCount
= cellModels
.size();
601 delete
= new Delete(key
);
603 CellModel valueToDeleteCell
= rowModel
.getCells().get(cellModelCount
-1);
604 byte[] valueToDeleteColumn
= valueToDeleteCell
.getColumn();
605 if (valueToDeleteColumn
== null) {
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
)
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();
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
)
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());
638 servlet
.getMetrics().incrementFailedDeleteRequests(1);
639 return Response
.status(Response
.Status
.BAD_REQUEST
)
641 .entity("Bad request: Column to delete incorrectly specified." + CRLF
)
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
);
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
);
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
)
672 if (LOG
.isTraceEnabled()) {
673 LOG
.trace("CHECK-AND-DELETE " + delete
.toString() + ", returns "
678 servlet
.getMetrics().incrementFailedDeleteRequests(1);
679 return Response
.status(Response
.Status
.NOT_MODIFIED
)
680 .type(MIMETYPE_TEXT
).entity(" Delete check failed." + CRLF
)
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
);
690 if (table
!= null) try {
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
) {
707 Append append
= null;
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
)
716 RowModel rowModel
= model
.getRows().get(0);
717 byte[] key
= rowModel
.getKey();
719 key
= rowspec
.getRow();
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
)
728 append
= new Append(key
);
729 append
.setReturnResults(returnResult
);
731 for (CellModel cell
: rowModel
.getCells()) {
732 byte[] col
= cell
.getColumn();
735 col
= rowspec
.getColumns()[i
++];
736 } catch (ArrayIndexOutOfBoundsException e
) {
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
)
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
)
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
);
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
)
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
);
784 if (table
!= null) try {
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
) {
801 Increment increment
= null;
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
)
810 RowModel rowModel
= model
.getRows().get(0);
811 byte[] key
= rowModel
.getKey();
813 key
= rowspec
.getRow();
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
)
822 increment
= new Increment(key
);
823 increment
.setReturnResults(returnResult
);
825 for (CellModel cell
: rowModel
.getCells()) {
826 byte[] col
= cell
.getColumn();
829 col
= rowspec
.getColumns()[i
++];
830 } catch (ArrayIndexOutOfBoundsException e
) {
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
)
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
)
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
);
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
)
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
);
881 if (table
!= null) try {
883 } catch (IOException ioe
) {
884 LOG
.debug("Exception received while closing the table " + table
.getName(), ioe
);