1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.hadoop.hbase.rest;
22
23 import java.io.IOException;
24 import java.net.URLDecoder;
25 import java.util.List;
26
27 import javax.ws.rs.Consumes;
28 import javax.ws.rs.DELETE;
29 import javax.ws.rs.GET;
30 import javax.ws.rs.POST;
31 import javax.ws.rs.PUT;
32 import javax.ws.rs.Produces;
33 import javax.ws.rs.WebApplicationException;
34 import javax.ws.rs.core.CacheControl;
35 import javax.ws.rs.core.Context;
36 import javax.ws.rs.core.HttpHeaders;
37 import javax.ws.rs.core.Response;
38 import javax.ws.rs.core.UriInfo;
39 import javax.ws.rs.core.Response.ResponseBuilder;
40
41 import org.apache.commons.logging.Log;
42 import org.apache.commons.logging.LogFactory;
43 import org.apache.hadoop.hbase.HConstants;
44 import org.apache.hadoop.hbase.KeyValue;
45 import org.apache.hadoop.hbase.client.Delete;
46 import org.apache.hadoop.hbase.client.HTableInterface;
47 import org.apache.hadoop.hbase.client.HTablePool;
48 import org.apache.hadoop.hbase.client.HTable;
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 public class RowResource extends ResourceBase {
56 private static final Log LOG = LogFactory.getLog(RowResource.class);
57
58 String tableName;
59 RowSpec rowspec;
60 CacheControl cacheControl;
61
62
63
64
65
66
67
68
69 public RowResource(String table, String rowspec, String versions)
70 throws IOException {
71 super();
72 this.tableName = table;
73 this.rowspec = new RowSpec(URLDecoder.decode(rowspec,
74 HConstants.UTF8_ENCODING));
75 if (versions != null) {
76 this.rowspec.setMaxVersions(Integer.valueOf(versions));
77 }
78 this.cacheControl = new CacheControl();
79 this.cacheControl.setMaxAge(servlet.getMaxAge(tableName));
80 this.cacheControl.setNoTransform(false);
81 }
82
83 @GET
84 @Produces({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF})
85 public Response get(final @Context UriInfo uriInfo) {
86 if (LOG.isDebugEnabled()) {
87 LOG.debug("GET " + uriInfo.getAbsolutePath());
88 }
89 servlet.getMetrics().incrementRequests(1);
90 try {
91 ResultGenerator generator =
92 ResultGenerator.fromRowSpec(tableName, rowspec, null);
93 if (!generator.hasNext()) {
94 throw new WebApplicationException(Response.Status.NOT_FOUND);
95 }
96 int count = 0;
97 CellSetModel model = new CellSetModel();
98 KeyValue value = generator.next();
99 byte[] rowKey = value.getRow();
100 RowModel rowModel = new RowModel(rowKey);
101 do {
102 if (!Bytes.equals(value.getRow(), rowKey)) {
103 model.addRow(rowModel);
104 rowKey = value.getRow();
105 rowModel = new RowModel(rowKey);
106 }
107 rowModel.addCell(
108 new CellModel(value.getFamily(), value.getQualifier(),
109 value.getTimestamp(), value.getValue()));
110 if (++count > rowspec.getMaxValues()) {
111 break;
112 }
113 value = generator.next();
114 } while (value != null);
115 model.addRow(rowModel);
116 ResponseBuilder response = Response.ok(model);
117 response.cacheControl(cacheControl);
118 return response.build();
119 } catch (IOException e) {
120 throw new WebApplicationException(e,
121 Response.Status.SERVICE_UNAVAILABLE);
122 }
123 }
124
125 @GET
126 @Produces(MIMETYPE_BINARY)
127 public Response getBinary(final @Context UriInfo uriInfo) {
128 if (LOG.isDebugEnabled()) {
129 LOG.debug("GET " + uriInfo.getAbsolutePath() + " as "+ MIMETYPE_BINARY);
130 }
131 servlet.getMetrics().incrementRequests(1);
132
133
134 if (!rowspec.hasColumns() || rowspec.getColumns().length > 1) {
135 throw new WebApplicationException(Response.Status.BAD_REQUEST);
136 }
137 try {
138 ResultGenerator generator =
139 ResultGenerator.fromRowSpec(tableName, rowspec, null);
140 if (!generator.hasNext()) {
141 throw new WebApplicationException(Response.Status.NOT_FOUND);
142 }
143 KeyValue value = generator.next();
144 ResponseBuilder response = Response.ok(value.getValue());
145 response.cacheControl(cacheControl);
146 response.header("X-Timestamp", value.getTimestamp());
147 return response.build();
148 } catch (IOException e) {
149 throw new WebApplicationException(e,
150 Response.Status.SERVICE_UNAVAILABLE);
151 }
152 }
153
154 Response update(final CellSetModel model, final boolean replace) {
155 servlet.getMetrics().incrementRequests(1);
156 HTablePool pool = servlet.getTablePool();
157 HTableInterface table = null;
158 try {
159 List<RowModel> rows = model.getRows();
160 table = pool.getTable(tableName);
161 ((HTable)table).setAutoFlush(false);
162 for (RowModel row: rows) {
163 byte[] key = row.getKey();
164 Put put = new Put(key);
165 for (CellModel cell: row.getCells()) {
166 byte [][] parts = KeyValue.parseColumn(cell.getColumn());
167 if (parts.length == 2 && parts[1].length > 0) {
168 put.add(parts[0], parts[1], cell.getTimestamp(), cell.getValue());
169 } else {
170 put.add(parts[0], null, cell.getTimestamp(), cell.getValue());
171 }
172 }
173 table.put(put);
174 if (LOG.isDebugEnabled()) {
175 LOG.debug("PUT " + put.toString());
176 }
177 }
178 ((HTable)table).setAutoFlush(true);
179 table.flushCommits();
180 ResponseBuilder response = Response.ok();
181 return response.build();
182 } catch (IOException e) {
183 throw new WebApplicationException(e,
184 Response.Status.SERVICE_UNAVAILABLE);
185 } finally {
186 if (table != null) {
187 pool.putTable(table);
188 }
189 }
190 }
191
192
193 Response updateBinary(final byte[] message, final HttpHeaders headers,
194 final boolean replace) {
195 servlet.getMetrics().incrementRequests(1);
196 HTablePool pool = servlet.getTablePool();
197 HTableInterface table = null;
198 try {
199 byte[] row = rowspec.getRow();
200 byte[][] columns = rowspec.getColumns();
201 byte[] column = null;
202 if (columns != null) {
203 column = columns[0];
204 }
205 long timestamp = HConstants.LATEST_TIMESTAMP;
206 List<String> vals = headers.getRequestHeader("X-Row");
207 if (vals != null && !vals.isEmpty()) {
208 row = Bytes.toBytes(vals.get(0));
209 }
210 vals = headers.getRequestHeader("X-Column");
211 if (vals != null && !vals.isEmpty()) {
212 column = Bytes.toBytes(vals.get(0));
213 }
214 vals = headers.getRequestHeader("X-Timestamp");
215 if (vals != null && !vals.isEmpty()) {
216 timestamp = Long.valueOf(vals.get(0));
217 }
218 if (column == null) {
219 throw new WebApplicationException(Response.Status.BAD_REQUEST);
220 }
221 Put put = new Put(row);
222 byte parts[][] = KeyValue.parseColumn(column);
223 if (parts.length == 2 && parts[1].length > 0) {
224 put.add(parts[0], parts[1], timestamp, message);
225 } else {
226 put.add(parts[0], null, timestamp, message);
227 }
228 table = pool.getTable(tableName);
229 table.put(put);
230 if (LOG.isDebugEnabled()) {
231 LOG.debug("PUT " + put.toString());
232 }
233 return Response.ok().build();
234 } catch (IOException e) {
235 throw new WebApplicationException(e,
236 Response.Status.SERVICE_UNAVAILABLE);
237 } finally {
238 if (table != null) {
239 pool.putTable(table);
240 }
241 }
242 }
243
244 @PUT
245 @Consumes({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF})
246 public Response put(final CellSetModel model,
247 final @Context UriInfo uriInfo) {
248 if (LOG.isDebugEnabled()) {
249 LOG.debug("PUT " + uriInfo.getAbsolutePath());
250 }
251 return update(model, true);
252 }
253
254 @PUT
255 @Consumes(MIMETYPE_BINARY)
256 public Response putBinary(final byte[] message,
257 final @Context UriInfo uriInfo, final @Context HttpHeaders headers) {
258 if (LOG.isDebugEnabled()) {
259 LOG.debug("PUT " + uriInfo.getAbsolutePath() + " as "+ MIMETYPE_BINARY);
260 }
261 return updateBinary(message, headers, true);
262 }
263
264 @POST
265 @Consumes({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF})
266 public Response post(final CellSetModel model,
267 final @Context UriInfo uriInfo) {
268 if (LOG.isDebugEnabled()) {
269 LOG.debug("POST " + uriInfo.getAbsolutePath());
270 }
271 return update(model, false);
272 }
273
274 @POST
275 @Consumes(MIMETYPE_BINARY)
276 public Response postBinary(final byte[] message,
277 final @Context UriInfo uriInfo, final @Context HttpHeaders headers) {
278 if (LOG.isDebugEnabled()) {
279 LOG.debug("POST " + uriInfo.getAbsolutePath() + " as "+MIMETYPE_BINARY);
280 }
281 return updateBinary(message, headers, false);
282 }
283
284 @DELETE
285 public Response delete(final @Context UriInfo uriInfo) {
286 if (LOG.isDebugEnabled()) {
287 LOG.debug("DELETE " + uriInfo.getAbsolutePath());
288 }
289 servlet.getMetrics().incrementRequests(1);
290 Delete delete = null;
291 if (rowspec.hasTimestamp())
292 delete = new Delete(rowspec.getRow(), rowspec.getTimestamp(), null);
293 else
294 delete = new Delete(rowspec.getRow());
295
296 for (byte[] column: rowspec.getColumns()) {
297 byte[][] split = KeyValue.parseColumn(column);
298 if (rowspec.hasTimestamp()) {
299 if (split.length == 2 && split[1].length != 0) {
300 delete.deleteColumns(split[0], split[1], rowspec.getTimestamp());
301 } else {
302 delete.deleteFamily(split[0], rowspec.getTimestamp());
303 }
304 } else {
305 if (split.length == 2 && split[1].length != 0) {
306 delete.deleteColumns(split[0], split[1]);
307 } else {
308 delete.deleteFamily(split[0]);
309 }
310 }
311 }
312 HTablePool pool = servlet.getTablePool();
313 HTableInterface table = null;
314 try {
315 table = pool.getTable(tableName);
316 table.delete(delete);
317 if (LOG.isDebugEnabled()) {
318 LOG.debug("DELETE " + delete.toString());
319 }
320 } catch (IOException e) {
321 throw new WebApplicationException(e,
322 Response.Status.SERVICE_UNAVAILABLE);
323 } finally {
324 if (table != null) {
325 pool.putTable(table);
326 }
327 }
328 return Response.ok().build();
329 }
330 }