View Javadoc

1   /*
2    *
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
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
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.
18   */
19  
20  package org.apache.hadoop.hbase.rest;
21  
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.List;
25  
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;
38  
39  import org.apache.commons.lang.StringUtils;
40  import org.apache.commons.logging.Log;
41  import org.apache.commons.logging.LogFactory;
42  import org.apache.hadoop.hbase.classification.InterfaceAudience;
43  import org.apache.hadoop.hbase.Cell;
44  import org.apache.hadoop.hbase.CellUtil;
45  import org.apache.hadoop.hbase.HConstants;
46  import org.apache.hadoop.hbase.KeyValue;
47  import org.apache.hadoop.hbase.client.Delete;
48  import org.apache.hadoop.hbase.client.HTableInterface;
49  import org.apache.hadoop.hbase.client.Put;
50  import org.apache.hadoop.hbase.rest.model.CellModel;
51  import org.apache.hadoop.hbase.rest.model.CellSetModel;
52  import org.apache.hadoop.hbase.rest.model.RowModel;
53  import org.apache.hadoop.hbase.util.Bytes;
54  
55  @InterfaceAudience.Private
56  public class RowResource extends ResourceBase {
57    private static final Log LOG = LogFactory.getLog(RowResource.class);
58  
59    static final String CHECK_PUT = "put";
60    static final String CHECK_DELETE = "delete";
61  
62    TableResource tableResource;
63    RowSpec rowspec;
64    private String check = null;
65  
66    /**
67     * Constructor
68     * @param tableResource
69     * @param rowspec
70     * @param versions
71     * @throws IOException
72     */
73    public RowResource(TableResource tableResource, String rowspec,
74        String versions, String check) throws IOException {
75      super();
76      this.tableResource = tableResource;
77      this.rowspec = new RowSpec(rowspec);
78      if (versions != null) {
79        this.rowspec.setMaxVersions(Integer.valueOf(versions));
80      }
81      this.check = check;
82    }
83  
84    @GET
85    @Produces({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF,
86      MIMETYPE_PROTOBUF_IETF})
87    public Response get(final @Context UriInfo uriInfo) {
88      if (LOG.isDebugEnabled()) {
89        LOG.debug("GET " + uriInfo.getAbsolutePath());
90      }
91      servlet.getMetrics().incrementRequests(1);
92      MultivaluedMap<String, String> params = uriInfo.getQueryParameters();
93      try {
94        ResultGenerator generator =
95          ResultGenerator.fromRowSpec(tableResource.getName(), rowspec, null,
96            !params.containsKey(NOCACHE_PARAM_NAME));
97        if (!generator.hasNext()) {
98          servlet.getMetrics().incrementFailedGetRequests(1);
99          return Response.status(Response.Status.NOT_FOUND)
100           .type(MIMETYPE_TEXT).entity("Not found" + CRLF)
101           .build();
102       }
103       int count = 0;
104       CellSetModel model = new CellSetModel();
105       Cell value = generator.next();
106       byte[] rowKey = CellUtil.cloneRow(value);
107       RowModel rowModel = new RowModel(rowKey);
108       do {
109         if (!Bytes.equals(CellUtil.cloneRow(value), rowKey)) {
110           model.addRow(rowModel);
111           rowKey = CellUtil.cloneRow(value);
112           rowModel = new RowModel(rowKey);
113         }
114         rowModel.addCell(new CellModel(CellUtil.cloneFamily(value), CellUtil.cloneQualifier(value),
115           value.getTimestamp(), CellUtil.cloneValue(value)));
116         if (++count > rowspec.getMaxValues()) {
117           break;
118         }
119         value = generator.next();
120       } while (value != null);
121       model.addRow(rowModel);
122       servlet.getMetrics().incrementSucessfulGetRequests(1);
123       return Response.ok(model).build();
124     } catch (Exception e) {
125       servlet.getMetrics().incrementFailedPutRequests(1);
126       return processException(e);
127     }
128   }
129 
130   @GET
131   @Produces(MIMETYPE_BINARY)
132   public Response getBinary(final @Context UriInfo uriInfo) {
133     if (LOG.isDebugEnabled()) {
134       LOG.debug("GET " + uriInfo.getAbsolutePath() + " as "+ MIMETYPE_BINARY);
135     }
136     servlet.getMetrics().incrementRequests(1);
137     // doesn't make sense to use a non specific coordinate as this can only
138     // return a single cell
139     if (!rowspec.hasColumns() || rowspec.getColumns().length > 1) {
140       servlet.getMetrics().incrementFailedGetRequests(1);
141       return Response.status(Response.Status.BAD_REQUEST).type(MIMETYPE_TEXT)
142           .entity("Bad request: Either 0 or more than 1 columns specified." + CRLF).build();
143     }
144     MultivaluedMap<String, String> params = uriInfo.getQueryParameters();
145     try {
146       ResultGenerator generator =
147         ResultGenerator.fromRowSpec(tableResource.getName(), rowspec, null,
148           !params.containsKey(NOCACHE_PARAM_NAME));
149       if (!generator.hasNext()) {
150         servlet.getMetrics().incrementFailedGetRequests(1);
151         return Response.status(Response.Status.NOT_FOUND)
152           .type(MIMETYPE_TEXT).entity("Not found" + CRLF)
153           .build();
154       }
155       Cell value = generator.next();
156       ResponseBuilder response = Response.ok(CellUtil.cloneValue(value));
157       response.header("X-Timestamp", value.getTimestamp());
158       servlet.getMetrics().incrementSucessfulGetRequests(1);
159       return response.build();
160     } catch (Exception e) {
161       servlet.getMetrics().incrementFailedGetRequests(1);
162       return processException(e);
163     }
164   }
165 
166   Response update(final CellSetModel model, final boolean replace) {
167     servlet.getMetrics().incrementRequests(1);
168     if (servlet.isReadOnly()) {
169       servlet.getMetrics().incrementFailedPutRequests(1);
170       return Response.status(Response.Status.FORBIDDEN)
171         .type(MIMETYPE_TEXT).entity("Forbidden" + CRLF)
172         .build();
173     }
174 
175     if (CHECK_PUT.equalsIgnoreCase(check)) {
176       return checkAndPut(model);
177     } else if (CHECK_DELETE.equalsIgnoreCase(check)) {
178       return checkAndDelete(model);
179     } else if (check != null && check.length() > 0) {
180       return Response.status(Response.Status.BAD_REQUEST)
181         .type(MIMETYPE_TEXT).entity("Invalid check value '" + check + "'" + CRLF)
182         .build();
183     }
184 
185     HTableInterface table = null;
186     try {
187       List<RowModel> rows = model.getRows();
188       List<Put> puts = new ArrayList<Put>();
189       for (RowModel row: rows) {
190         byte[] key = row.getKey();
191         if (key == null) {
192           key = rowspec.getRow();
193         }
194         if (key == null) {
195           servlet.getMetrics().incrementFailedPutRequests(1);
196           return Response.status(Response.Status.BAD_REQUEST)
197             .type(MIMETYPE_TEXT).entity("Bad request: Row key not specified." + CRLF)
198             .build();
199         }
200         Put put = new Put(key);
201         int i = 0;
202         for (CellModel cell: row.getCells()) {
203           byte[] col = cell.getColumn();
204           if (col == null) try {
205             col = rowspec.getColumns()[i++];
206           } catch (ArrayIndexOutOfBoundsException e) {
207             col = null;
208           }
209           if (col == null) {
210             servlet.getMetrics().incrementFailedPutRequests(1);
211             return Response.status(Response.Status.BAD_REQUEST)
212               .type(MIMETYPE_TEXT).entity("Bad request: Column found to be null." + CRLF)
213               .build();
214           }
215           byte [][] parts = KeyValue.parseColumn(col);
216           if (parts.length != 2) {
217             return Response.status(Response.Status.BAD_REQUEST)
218               .type(MIMETYPE_TEXT).entity("Bad request" + CRLF)
219               .build();
220           }
221           put.addImmutable(parts[0], parts[1], cell.getTimestamp(), cell.getValue());
222         }
223         puts.add(put);
224         if (LOG.isDebugEnabled()) {
225           LOG.debug("PUT " + put.toString());
226         }
227       }
228       table = servlet.getTable(tableResource.getName());
229       table.put(puts);
230       table.flushCommits();
231       ResponseBuilder response = Response.ok();
232       servlet.getMetrics().incrementSucessfulPutRequests(1);
233       return response.build();
234     } catch (Exception e) {
235       servlet.getMetrics().incrementFailedPutRequests(1);
236       return processException(e);
237     } finally {
238       if (table != null) try {
239         table.close();
240       } catch (IOException ioe) {
241         LOG.debug("Exception received while closing the table", ioe);
242       }
243     }
244   }
245 
246   // This currently supports only update of one row at a time.
247   Response updateBinary(final byte[] message, final HttpHeaders headers,
248       final boolean replace) {
249     servlet.getMetrics().incrementRequests(1);
250     if (servlet.isReadOnly()) {
251       servlet.getMetrics().incrementFailedPutRequests(1);
252       return Response.status(Response.Status.FORBIDDEN)
253         .type(MIMETYPE_TEXT).entity("Forbidden" + CRLF)
254         .build();
255     }
256     HTableInterface table = null;
257     try {
258       byte[] row = rowspec.getRow();
259       byte[][] columns = rowspec.getColumns();
260       byte[] column = null;
261       if (columns != null) {
262         column = columns[0];
263       }
264       long timestamp = HConstants.LATEST_TIMESTAMP;
265       List<String> vals = headers.getRequestHeader("X-Row");
266       if (vals != null && !vals.isEmpty()) {
267         row = Bytes.toBytes(vals.get(0));
268       }
269       vals = headers.getRequestHeader("X-Column");
270       if (vals != null && !vals.isEmpty()) {
271         column = Bytes.toBytes(vals.get(0));
272       }
273       vals = headers.getRequestHeader("X-Timestamp");
274       if (vals != null && !vals.isEmpty()) {
275         timestamp = Long.valueOf(vals.get(0));
276       }
277       if (column == null) {
278         servlet.getMetrics().incrementFailedPutRequests(1);
279         return Response.status(Response.Status.BAD_REQUEST)
280             .type(MIMETYPE_TEXT).entity("Bad request: Column found to be null." + CRLF)
281             .build();
282       }
283       Put put = new Put(row);
284       byte parts[][] = KeyValue.parseColumn(column);
285       if (parts.length != 2) {
286         return Response.status(Response.Status.BAD_REQUEST)
287           .type(MIMETYPE_TEXT).entity("Bad request" + CRLF)
288           .build();
289       }
290       put.addImmutable(parts[0], parts[1], timestamp, message);
291       table = servlet.getTable(tableResource.getName());
292       table.put(put);
293       if (LOG.isDebugEnabled()) {
294         LOG.debug("PUT " + put.toString());
295       }
296       servlet.getMetrics().incrementSucessfulPutRequests(1);
297       return Response.ok().build();
298     } catch (Exception e) {
299       servlet.getMetrics().incrementFailedPutRequests(1);
300       return processException(e);
301     } finally {
302       if (table != null) try {
303         table.close();
304       } catch (IOException ioe) {
305         LOG.debug(ioe);
306       }
307     }
308   }
309 
310   @PUT
311   @Consumes({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF,
312     MIMETYPE_PROTOBUF_IETF})
313   public Response put(final CellSetModel model,
314       final @Context UriInfo uriInfo) {
315     if (LOG.isDebugEnabled()) {
316       LOG.debug("PUT " + uriInfo.getAbsolutePath()
317         + " " + uriInfo.getQueryParameters());
318     }
319     return update(model, true);
320   }
321 
322   @PUT
323   @Consumes(MIMETYPE_BINARY)
324   public Response putBinary(final byte[] message,
325       final @Context UriInfo uriInfo, final @Context HttpHeaders headers) {
326     if (LOG.isDebugEnabled()) {
327       LOG.debug("PUT " + uriInfo.getAbsolutePath() + " as "+ MIMETYPE_BINARY);
328     }
329     return updateBinary(message, headers, true);
330   }
331 
332   @POST
333   @Consumes({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF,
334     MIMETYPE_PROTOBUF_IETF})
335   public Response post(final CellSetModel model,
336       final @Context UriInfo uriInfo) {
337     if (LOG.isDebugEnabled()) {
338       LOG.debug("POST " + uriInfo.getAbsolutePath()
339         + " " + uriInfo.getQueryParameters());
340     }
341     return update(model, false);
342   }
343 
344   @POST
345   @Consumes(MIMETYPE_BINARY)
346   public Response postBinary(final byte[] message,
347       final @Context UriInfo uriInfo, final @Context HttpHeaders headers) {
348     if (LOG.isDebugEnabled()) {
349       LOG.debug("POST " + uriInfo.getAbsolutePath() + " as "+MIMETYPE_BINARY);
350     }
351     return updateBinary(message, headers, false);
352   }
353 
354   @DELETE
355   public Response delete(final @Context UriInfo uriInfo) {
356     if (LOG.isDebugEnabled()) {
357       LOG.debug("DELETE " + uriInfo.getAbsolutePath());
358     }
359     servlet.getMetrics().incrementRequests(1);
360     if (servlet.isReadOnly()) {
361       servlet.getMetrics().incrementFailedDeleteRequests(1);
362       return Response.status(Response.Status.FORBIDDEN)
363         .type(MIMETYPE_TEXT).entity("Forbidden" + CRLF)
364         .build();
365     }
366     Delete delete = null;
367     if (rowspec.hasTimestamp())
368       delete = new Delete(rowspec.getRow(), rowspec.getTimestamp());
369     else
370       delete = new Delete(rowspec.getRow());
371 
372     for (byte[] column: rowspec.getColumns()) {
373       byte[][] split = KeyValue.parseColumn(column);
374       if (rowspec.hasTimestamp()) {
375         if (split.length == 1) {
376           delete.deleteFamily(split[0], rowspec.getTimestamp());
377         } else if (split.length == 2) {
378           delete.deleteColumns(split[0], split[1], rowspec.getTimestamp());
379         } else {
380           return Response.status(Response.Status.BAD_REQUEST)
381             .type(MIMETYPE_TEXT).entity("Bad request" + CRLF)
382             .build();
383         }
384       } else {
385         if (split.length == 1) {
386           delete.deleteFamily(split[0]);
387         } else if (split.length == 2) {
388           delete.deleteColumns(split[0], split[1]);
389         } else {
390           return Response.status(Response.Status.BAD_REQUEST)
391             .type(MIMETYPE_TEXT).entity("Bad request" + CRLF)
392             .build();
393         }
394       }
395     }
396     HTableInterface table = null;
397     try {
398       table = servlet.getTable(tableResource.getName());
399       table.delete(delete);
400       servlet.getMetrics().incrementSucessfulDeleteRequests(1);
401       if (LOG.isDebugEnabled()) {
402         LOG.debug("DELETE " + delete.toString());
403       }
404     } catch (Exception e) {
405       servlet.getMetrics().incrementFailedDeleteRequests(1);
406       return processException(e);
407     } finally {
408       if (table != null) try {
409         table.close();
410       } catch (IOException ioe) {
411         LOG.debug(ioe);
412       }
413     }
414     return Response.ok().build();
415   }
416 
417   /**
418    * Validates the input request parameters, parses columns from CellSetModel,
419    * and invokes checkAndPut on HTable.
420    *
421    * @param model instance of CellSetModel
422    * @return Response 200 OK, 304 Not modified, 400 Bad request
423    */
424   Response checkAndPut(final CellSetModel model) {
425     HTableInterface table = null;
426     try {
427       table = servlet.getTable(tableResource.getName());
428       if (model.getRows().size() != 1) {
429         servlet.getMetrics().incrementFailedPutRequests(1);
430         return Response.status(Response.Status.BAD_REQUEST).type(MIMETYPE_TEXT)
431             .entity("Bad request: Number of rows specified is not 1." + CRLF).build();
432       }
433 
434       RowModel rowModel = model.getRows().get(0);
435       byte[] key = rowModel.getKey();
436       if (key == null) {
437         key = rowspec.getRow();
438       }
439 
440       List<CellModel> cellModels = rowModel.getCells();
441       int cellModelCount = cellModels.size();
442       if (key == null || cellModelCount <= 1) {
443         servlet.getMetrics().incrementFailedPutRequests(1);
444         return Response
445             .status(Response.Status.BAD_REQUEST)
446             .type(MIMETYPE_TEXT)
447             .entity(
448               "Bad request: Either row key is null or no data found for columns specified." + CRLF)
449             .build();
450       }
451 
452       Put put = new Put(key);
453       boolean retValue;
454       CellModel valueToCheckCell = cellModels.get(cellModelCount - 1);
455       byte[] valueToCheckColumn = valueToCheckCell.getColumn();
456       byte[][] valueToPutParts = KeyValue.parseColumn(valueToCheckColumn);
457       if (valueToPutParts.length == 2 && valueToPutParts[1].length > 0) {
458         CellModel valueToPutCell = null;
459 
460         // Copy all the cells to the Put request
461         // and track if the check cell's latest value is also sent
462         for (int i = 0, n = cellModelCount - 1; i < n ; i++) {
463           CellModel cell = cellModels.get(i);
464           byte[] col = cell.getColumn();
465 
466           if (col == null) {
467             servlet.getMetrics().incrementFailedPutRequests(1);
468             return Response.status(Response.Status.BAD_REQUEST)
469                     .type(MIMETYPE_TEXT).entity("Bad request: Column found to be null." + CRLF)
470                     .build();
471           }
472 
473           byte [][] parts = KeyValue.parseColumn(col);
474 
475           if (parts.length != 2) {
476             return Response.status(Response.Status.BAD_REQUEST)
477                     .type(MIMETYPE_TEXT).entity("Bad request" + CRLF)
478                     .build();
479           }
480           put.addImmutable(parts[0], parts[1], cell.getTimestamp(), cell.getValue());
481 
482           if(Bytes.equals(col,
483                   valueToCheckCell.getColumn())) {
484             valueToPutCell = cell;
485           }
486         }
487 
488         if (valueToPutCell == null) {
489           servlet.getMetrics().incrementFailedPutRequests(1);
490           return Response.status(Response.Status.BAD_REQUEST).type(MIMETYPE_TEXT)
491               .entity("Bad request: The column to put and check do not match." + CRLF).build();
492         } else {
493           retValue = table.checkAndPut(key, valueToPutParts[0], valueToPutParts[1],
494             valueToCheckCell.getValue(), put);
495         }
496       } else {
497         servlet.getMetrics().incrementFailedPutRequests(1);
498         return Response.status(Response.Status.BAD_REQUEST)
499           .type(MIMETYPE_TEXT).entity("Bad request: Column incorrectly specified." + CRLF)
500           .build();
501       }
502 
503       if (LOG.isDebugEnabled()) {
504         LOG.debug("CHECK-AND-PUT " + put.toString() + ", returns " + retValue);
505       }
506       if (!retValue) {
507         servlet.getMetrics().incrementFailedPutRequests(1);
508         return Response.status(Response.Status.NOT_MODIFIED)
509           .type(MIMETYPE_TEXT).entity("Value not Modified" + CRLF)
510           .build();
511       }
512       table.flushCommits();
513       ResponseBuilder response = Response.ok();
514       servlet.getMetrics().incrementSucessfulPutRequests(1);
515       return response.build();
516     } catch (Exception e) {
517       servlet.getMetrics().incrementFailedPutRequests(1);
518       return processException(e);
519     } finally {
520       if (table != null) try {
521         table.close();
522       } catch (IOException ioe) { 
523         LOG.debug("Exception received while closing the table", ioe);
524       }
525     }
526   }
527 
528   /**
529    * Validates the input request parameters, parses columns from CellSetModel,
530    * and invokes checkAndDelete on HTable.
531    *
532    * @param model instance of CellSetModel
533    * @return Response 200 OK, 304 Not modified, 400 Bad request
534    */
535   Response checkAndDelete(final CellSetModel model) {
536     HTableInterface table = null;
537     Delete delete = null;
538     try {
539       table = servlet.getTable(tableResource.getName());
540       if (model.getRows().size() != 1) {
541         servlet.getMetrics().incrementFailedDeleteRequests(1);
542         return Response.status(Response.Status.BAD_REQUEST)
543           .type(MIMETYPE_TEXT).entity("Bad request" + CRLF)
544           .build();
545       }
546       RowModel rowModel = model.getRows().get(0);
547       byte[] key = rowModel.getKey();
548       if (key == null) {
549         key = rowspec.getRow();
550       }
551       if (key == null) {
552         servlet.getMetrics().incrementFailedDeleteRequests(1);
553         return Response.status(Response.Status.BAD_REQUEST)
554           .type(MIMETYPE_TEXT).entity("Bad request: Row key found to be null." + CRLF)
555           .build();
556       }
557 
558       List<CellModel> cellModels = rowModel.getCells();
559       int cellModelCount = cellModels.size();
560 
561       delete = new Delete(key);
562       boolean retValue;
563       CellModel valueToDeleteCell = rowModel.getCells().get(cellModelCount -1);
564       byte[] valueToDeleteColumn = valueToDeleteCell.getColumn();
565       if (valueToDeleteColumn == null) {
566         try {
567           valueToDeleteColumn = rowspec.getColumns()[0];
568         } catch (final ArrayIndexOutOfBoundsException e) {
569           servlet.getMetrics().incrementFailedDeleteRequests(1);
570           return Response.status(Response.Status.BAD_REQUEST)
571             .type(MIMETYPE_TEXT).entity("Bad request: Column not specified for check." + CRLF)
572             .build();
573         }
574       }
575 
576       byte[][] parts ;
577       // Copy all the cells to the Delete request if extra cells are sent
578       if(cellModelCount > 1) {
579         for (int i = 0, n = cellModelCount - 1; i < n; i++) {
580           CellModel cell = cellModels.get(i);
581           byte[] col = cell.getColumn();
582 
583           if (col == null) {
584             servlet.getMetrics().incrementFailedPutRequests(1);
585             return Response.status(Response.Status.BAD_REQUEST)
586                     .type(MIMETYPE_TEXT).entity("Bad request: Column found to be null." + CRLF)
587                     .build();
588           }
589 
590           parts = KeyValue.parseColumn(col);
591 
592           if (parts.length == 1) {
593             // Only Column Family is specified
594             delete.deleteFamily(parts[0], cell.getTimestamp());
595           } else if (parts.length == 2) {
596             delete.deleteColumn(parts[0], parts[1], cell.getTimestamp());
597           } else {
598             servlet.getMetrics().incrementFailedDeleteRequests(1);
599             return Response.status(Response.Status.BAD_REQUEST)
600                     .type(MIMETYPE_TEXT)
601                     .entity("Bad request: Column to delete incorrectly specified." + CRLF)
602                     .build();
603           }
604         }
605       }
606 
607       parts = KeyValue.parseColumn(valueToDeleteColumn);
608       if (parts.length == 2) {
609         if (parts[1].length != 0) {
610           // To support backcompat of deleting a cell
611           // if that is the only cell passed to the rest api
612           if(cellModelCount == 1) {
613             delete.deleteColumns(parts[0], parts[1]);
614           }
615           retValue = table.checkAndDelete(key, parts[0], parts[1],
616             valueToDeleteCell.getValue(), delete);
617         } else {
618           // The case of empty qualifier.
619           if(cellModelCount == 1) {
620             delete.deleteColumns(parts[0], Bytes.toBytes(StringUtils.EMPTY));
621           }
622           retValue = table.checkAndDelete(key, parts[0], Bytes.toBytes(StringUtils.EMPTY),
623             valueToDeleteCell.getValue(), delete);
624         }
625       } else {
626         servlet.getMetrics().incrementFailedDeleteRequests(1);
627         return Response.status(Response.Status.BAD_REQUEST)
628           .type(MIMETYPE_TEXT).entity("Bad request: Column to check incorrectly specified." + CRLF)
629           .build();
630       }
631 
632       if (LOG.isDebugEnabled()) {
633         LOG.debug("CHECK-AND-DELETE " + delete.toString() + ", returns "
634           + retValue);
635       }
636 
637       if (!retValue) {
638         servlet.getMetrics().incrementFailedDeleteRequests(1);
639         return Response.status(Response.Status.NOT_MODIFIED)
640             .type(MIMETYPE_TEXT).entity(" Delete check failed." + CRLF)
641             .build();
642       }
643       table.flushCommits();
644       ResponseBuilder response = Response.ok();
645       servlet.getMetrics().incrementSucessfulDeleteRequests(1);
646       return response.build();
647     } catch (Exception e) {
648       servlet.getMetrics().incrementFailedDeleteRequests(1);
649       return processException(e);
650     } finally {
651       if (table != null) try {
652         table.close();
653       } catch (IOException ioe) {
654         LOG.debug("Exception received while closing the table", ioe);
655       }
656     }
657   }
658 }