View Javadoc

1   /*
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  package org.apache.hadoop.hbase.rest;
22  
23  import java.io.IOException;
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.WebApplicationException;
33  import javax.ws.rs.core.Context;
34  import javax.ws.rs.core.HttpHeaders;
35  import javax.ws.rs.core.Response;
36  import javax.ws.rs.core.UriInfo;
37  import javax.ws.rs.core.Response.ResponseBuilder;
38  
39  import org.apache.commons.logging.Log;
40  import org.apache.commons.logging.LogFactory;
41  import org.apache.hadoop.hbase.HConstants;
42  import org.apache.hadoop.hbase.KeyValue;
43  import org.apache.hadoop.hbase.client.Delete;
44  import org.apache.hadoop.hbase.client.HTableInterface;
45  import org.apache.hadoop.hbase.client.HTablePool;
46  import org.apache.hadoop.hbase.client.HTable;
47  import org.apache.hadoop.hbase.client.Put;
48  import org.apache.hadoop.hbase.rest.model.CellModel;
49  import org.apache.hadoop.hbase.rest.model.CellSetModel;
50  import org.apache.hadoop.hbase.rest.model.RowModel;
51  import org.apache.hadoop.hbase.rest.transform.Transform;
52  import org.apache.hadoop.hbase.util.Bytes;
53  
54  public class RowResource extends ResourceBase {
55    private static final Log LOG = LogFactory.getLog(RowResource.class);
56  
57    TableResource tableResource;
58    RowSpec rowspec;
59  
60    /**
61     * Constructor
62     * @param tableResource
63     * @param rowspec
64     * @param versions
65     * @throws IOException
66     */
67    public RowResource(TableResource tableResource, String rowspec,
68        String versions) throws IOException {
69      super();
70      this.tableResource = tableResource;
71      this.rowspec = new RowSpec(rowspec);
72      if (versions != null) {
73        this.rowspec.setMaxVersions(Integer.valueOf(versions));
74      }
75    }
76  
77    @GET
78    @Produces({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF})
79    public Response get(final @Context UriInfo uriInfo) {
80      if (LOG.isDebugEnabled()) {
81        LOG.debug("GET " + uriInfo.getAbsolutePath());
82      }
83      servlet.getMetrics().incrementRequests(1);
84      try {
85        ResultGenerator generator =
86          ResultGenerator.fromRowSpec(tableResource.getName(), rowspec, null);
87        if (!generator.hasNext()) {
88          throw new WebApplicationException(Response.Status.NOT_FOUND);
89        }
90        int count = 0;
91        CellSetModel model = new CellSetModel();
92        KeyValue value = generator.next();
93        byte[] rowKey = value.getRow();
94        RowModel rowModel = new RowModel(rowKey);
95        do {
96          if (!Bytes.equals(value.getRow(), rowKey)) {
97            model.addRow(rowModel);
98            rowKey = value.getRow();
99            rowModel = new RowModel(rowKey);
100         }
101         byte[] family = value.getFamily();
102         byte[] qualifier = value.getQualifier();
103         byte[] data = tableResource.transform(family, qualifier,
104           value.getValue(), Transform.Direction.OUT);
105         rowModel.addCell(new CellModel(family, qualifier,
106           value.getTimestamp(), data));
107         if (++count > rowspec.getMaxValues()) {
108           break;
109         }
110         value = generator.next();
111       } while (value != null);
112       model.addRow(rowModel);
113       return Response.ok(model).build();
114     } catch (IOException e) {
115       throw new WebApplicationException(e,
116                   Response.Status.SERVICE_UNAVAILABLE);
117     }
118   }
119 
120   @GET
121   @Produces(MIMETYPE_BINARY)
122   public Response getBinary(final @Context UriInfo uriInfo) {
123     if (LOG.isDebugEnabled()) {
124       LOG.debug("GET " + uriInfo.getAbsolutePath() + " as "+ MIMETYPE_BINARY);
125     }
126     servlet.getMetrics().incrementRequests(1);
127     // doesn't make sense to use a non specific coordinate as this can only
128     // return a single cell
129     if (!rowspec.hasColumns() || rowspec.getColumns().length > 1) {
130       throw new WebApplicationException(Response.Status.BAD_REQUEST);
131     }
132     try {
133       ResultGenerator generator =
134         ResultGenerator.fromRowSpec(tableResource.getName(), rowspec, null);
135       if (!generator.hasNext()) {
136         throw new WebApplicationException(Response.Status.NOT_FOUND);
137       }
138       KeyValue value = generator.next();
139       byte[] family = value.getFamily();
140       byte[] qualifier = value.getQualifier();
141       byte[] data = tableResource.transform(family, qualifier,
142         value.getValue(), Transform.Direction.OUT);
143       ResponseBuilder response = Response.ok(data);
144       response.header("X-Timestamp", value.getTimestamp());
145       return response.build();
146     } catch (IOException e) {
147       throw new WebApplicationException(e,
148                   Response.Status.SERVICE_UNAVAILABLE);
149     }
150   }
151 
152   Response update(final CellSetModel model, final boolean replace) {
153     servlet.getMetrics().incrementRequests(1);
154     if (servlet.isReadOnly()) {
155       throw new WebApplicationException(Response.Status.FORBIDDEN);
156     }
157     HTablePool pool = servlet.getTablePool();
158     HTableInterface table = null;
159     try {
160       List<RowModel> rows = model.getRows();
161       table = pool.getTable(tableResource.getName());
162       ((HTable)table).setAutoFlush(false);
163       for (RowModel row: rows) {
164         byte[] key = row.getKey();
165         Put put = new Put(key);
166         for (CellModel cell: row.getCells()) {
167           byte [][] parts = KeyValue.parseColumn(cell.getColumn());
168           if (parts.length == 2 && parts[1].length > 0) {
169             put.add(parts[0], parts[1], cell.getTimestamp(),
170               tableResource.transform(parts[0], parts[1], cell.getValue(),
171                 Transform.Direction.IN));
172           } else {
173             put.add(parts[0], null, cell.getTimestamp(),
174               tableResource.transform(parts[0], null, cell.getValue(),
175                 Transform.Direction.IN));
176           }
177         }
178         table.put(put);
179         if (LOG.isDebugEnabled()) {
180           LOG.debug("PUT " + put.toString());
181         }
182       }
183       ((HTable)table).setAutoFlush(true);
184       table.flushCommits();
185       ResponseBuilder response = Response.ok();
186       return response.build();
187     } catch (IOException e) {
188       throw new WebApplicationException(e,
189                   Response.Status.SERVICE_UNAVAILABLE);
190     } finally {
191       if (table != null) {
192         pool.putTable(table);
193       }
194     }
195   }
196 
197   // This currently supports only update of one row at a time.
198   Response updateBinary(final byte[] message, final HttpHeaders headers,
199       final boolean replace) {
200     servlet.getMetrics().incrementRequests(1);
201     if (servlet.isReadOnly()) {
202       throw new WebApplicationException(Response.Status.FORBIDDEN);
203     }
204     HTablePool pool = servlet.getTablePool();
205     HTableInterface table = null;
206     try {
207       byte[] row = rowspec.getRow();
208       byte[][] columns = rowspec.getColumns();
209       byte[] column = null;
210       if (columns != null) {
211         column = columns[0];
212       }
213       long timestamp = HConstants.LATEST_TIMESTAMP;
214       List<String> vals = headers.getRequestHeader("X-Row");
215       if (vals != null && !vals.isEmpty()) {
216         row = Bytes.toBytes(vals.get(0));
217       }
218       vals = headers.getRequestHeader("X-Column");
219       if (vals != null && !vals.isEmpty()) {
220         column = Bytes.toBytes(vals.get(0));
221       }
222       vals = headers.getRequestHeader("X-Timestamp");
223       if (vals != null && !vals.isEmpty()) {
224         timestamp = Long.valueOf(vals.get(0));
225       }
226       if (column == null) {
227         throw new WebApplicationException(Response.Status.BAD_REQUEST);
228       }
229       Put put = new Put(row);
230       byte parts[][] = KeyValue.parseColumn(column);
231       if (parts.length == 2 && parts[1].length > 0) {
232         put.add(parts[0], parts[1], timestamp,
233           tableResource.transform(parts[0], parts[1], message,
234             Transform.Direction.IN));
235       } else {
236         put.add(parts[0], null, timestamp,
237           tableResource.transform(parts[0], null, message,
238             Transform.Direction.IN));
239       }
240       table = pool.getTable(tableResource.getName());
241       table.put(put);
242       if (LOG.isDebugEnabled()) {
243         LOG.debug("PUT " + put.toString());
244       }
245       return Response.ok().build();
246     } catch (IOException e) {
247       throw new WebApplicationException(e,
248                   Response.Status.SERVICE_UNAVAILABLE);
249     } finally {
250       if (table != null) {
251         pool.putTable(table);
252       }
253     }
254   }
255 
256   @PUT
257   @Consumes({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF})
258   public Response put(final CellSetModel model,
259       final @Context UriInfo uriInfo) {
260     if (LOG.isDebugEnabled()) {
261       LOG.debug("PUT " + uriInfo.getAbsolutePath());
262     }
263     return update(model, true);
264   }
265 
266   @PUT
267   @Consumes(MIMETYPE_BINARY)
268   public Response putBinary(final byte[] message,
269       final @Context UriInfo uriInfo, final @Context HttpHeaders headers) {
270     if (LOG.isDebugEnabled()) {
271       LOG.debug("PUT " + uriInfo.getAbsolutePath() + " as "+ MIMETYPE_BINARY);
272     }
273     return updateBinary(message, headers, true);
274   }
275 
276   @POST
277   @Consumes({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF})
278   public Response post(final CellSetModel model,
279       final @Context UriInfo uriInfo) {
280     if (LOG.isDebugEnabled()) {
281       LOG.debug("POST " + uriInfo.getAbsolutePath());
282     }
283     return update(model, false);
284   }
285 
286   @POST
287   @Consumes(MIMETYPE_BINARY)
288   public Response postBinary(final byte[] message,
289       final @Context UriInfo uriInfo, final @Context HttpHeaders headers) {
290     if (LOG.isDebugEnabled()) {
291       LOG.debug("POST " + uriInfo.getAbsolutePath() + " as "+MIMETYPE_BINARY);
292     }
293     return updateBinary(message, headers, false);
294   }
295 
296   @DELETE
297   public Response delete(final @Context UriInfo uriInfo) {
298     if (LOG.isDebugEnabled()) {
299       LOG.debug("DELETE " + uriInfo.getAbsolutePath());
300     }
301     servlet.getMetrics().incrementRequests(1);
302     if (servlet.isReadOnly()) {
303       throw new WebApplicationException(Response.Status.FORBIDDEN);
304     }
305     Delete delete = null;
306     if (rowspec.hasTimestamp())
307       delete = new Delete(rowspec.getRow(), rowspec.getTimestamp(), null);
308     else
309       delete = new Delete(rowspec.getRow());
310 
311     for (byte[] column: rowspec.getColumns()) {
312       byte[][] split = KeyValue.parseColumn(column);
313       if (rowspec.hasTimestamp()) {
314         if (split.length == 2 && split[1].length != 0) {
315           delete.deleteColumns(split[0], split[1], rowspec.getTimestamp());
316         } else {
317           delete.deleteFamily(split[0], rowspec.getTimestamp());
318         }
319       } else {
320         if (split.length == 2 && split[1].length != 0) {
321           delete.deleteColumns(split[0], split[1]);
322         } else {
323           delete.deleteFamily(split[0]);
324         }
325       }
326     }
327     HTablePool pool = servlet.getTablePool();
328     HTableInterface table = null;
329     try {
330       table = pool.getTable(tableResource.getName());
331       table.delete(delete);
332       if (LOG.isDebugEnabled()) {
333         LOG.debug("DELETE " + delete.toString());
334       }
335     } catch (IOException e) {
336       throw new WebApplicationException(e, 
337                   Response.Status.SERVICE_UNAVAILABLE);
338     } finally {
339       if (table != null) {
340         pool.putTable(table);
341       }
342     }
343     return Response.ok().build();
344   }
345 }